cv.cm

Lập trình · Ngôn ngữ lập trình

Học Dart cơ bản cho người mới bắt đầu

8 phút đọc

Trả lời nhanh

Dart là ngôn ngữ hướng đối tượng, kiểu tĩnh do Google phát triển, nổi tiếng nhất là ngôn ngữ của framework ứng dụng Flutter. Dart có sound null safety, biên dịch ra mã native cũng như JavaScript hoặc WebAssembly, và hỗ trợ hot reload nhanh khi phát triển. Trang này hướng dẫn cài đặt, kiểu dữ liệu, null safety, cấu trúc điều khiển, hàm, collection, lớp, mã bất đồng bộ và xử lý lỗi.

Dart là gì và dùng để làm gì

Dart là ngôn ngữ lập trình tối ưu cho phía client do Google phát triển. Dart có kiểu tĩnh kèm suy luận kiểu, có sound null safety, và biên dịch cả sang mã máy native (cho di động, desktop và máy chủ) lẫn JavaScript hoặc WebAssembly (cho web). Khi phát triển, Dart chạy trên máy ảo có hot reload, nên thay đổi hiện ra trong ứng dụng đang chạy chỉ sau vài giây.

Hầu hết mọi người học Dart để xây dựng ứng dụng bằng Flutter, bộ công cụ UI của Google cho Android, iOS, web, Windows, macOS và Linux từ một mã nguồn duy nhất. Dart cũng được dùng cho công cụ dòng lệnh và dịch vụ backend.

Cài đặt Dart và chạy Hello, world

Cài Dart SDK từ dart.dev (hoặc cài Flutter, bộ này đã kèm sẵn Dart). Mọi chương trình Dart đều bắt đầu từ hàm main ở cấp cao nhất.

// hello.dart
void main() {
  print('Hello, world!');
}
dart run hello.dart
# Hello, world!
dart compile exe hello.dart   # builds a native executable
dart create my_app            # creates a new console project

Bạn cũng có thể chạy thử mọi ví dụ trong trang này trên DartPad, trình soạn thảo trên trình duyệt tại dartpad.dev.

Biến và các kiểu dữ liệu cơ bản

Dùng var để trình biên dịch tự suy ra kiểu, final cho biến chỉ gán một lần, và const cho hằng số xác định lúc biên dịch. Bạn luôn có thể ghi kiểu tường minh thay vào đó.

void main() {
  var name = 'Dart';        // inferred as String
  int year = 2011;          // explicit type
  final now = DateTime.now(); // set once, at run time
  const pi = 3.14159;       // compile-time constant
  double ratio = 2;         // int literal becomes 2.0
  bool ok = year > 2000;
  print('$name since $year, pi=${pi.toStringAsFixed(2)}, ok=$ok, ratio=$ratio');
  // Dart since 2011, pi=3.14, ok=true, ratio=2.0
  print(now.year > 2000);   // true
}
KiểuVí dụGhi chú
int42số nguyên; 64 bit trên các nền tảng native
double3.14số thực dấu phẩy động 64 bit
num7 or 7.5kiểu cha của int và double
booltrueđiều kiện chỉ chấp nhận true và false
String'hi'UTF-16; hỗ trợ nội suy $x và ${expr}
dynamicbất kỳ giá trị nàotắt kiểm tra tĩnh; nên tránh trong hầu hết mã

Trên web, giá trị int được biểu diễn như số JavaScript, nên các số nguyên rất lớn có thể mất độ chính xác ở đó.

Sound null safety

Từ Dart 3, toàn bộ mã Dart dùng sound null safety. Biến kiểu String không bao giờ là null; để cho phép null, hãy viết String?. Trình biên dịch kiểm tra mọi chỗ dùng, nên lỗi null được phát hiện trước khi chương trình chạy.

int lengthOf(String? s) {
  // return s.length;         // compile error: s might be null
  if (s == null) return 0;
  return s.length;            // promoted to String after the check
}
void main() {
  String? nickname;
  print(nickname?.length);    // null (null-aware access)
  print(nickname ?? 'guest'); // guest (default if null)
  nickname ??= 'dee';         // assign only if null
  print(lengthOf(nickname));  // 3
  late String config;         // initialized later, checked at run time
  config = 'loaded';
  print(config);              // loaded
}

Cấu trúc điều khiển

Dart có if, for, while và do-while quen thuộc, cùng vòng lặp for-in trên mọi iterable. Từ Dart 3, switch hỗ trợ so khớp mẫu (pattern matching) và có thể dùng như một biểu thức.

void main() {
  var scores = [72, 95, 48];
  for (var s in scores) {
    if (s >= 90) {
      print('$s excellent');
    } else if (s >= 50) {
      print('$s pass');
    } else {
      print('$s fail');
    }
  }
  for (var i = 0; i < 3; i++) {
    print('i = $i');
  }
  var day = 6;
  var kind = switch (day) {
    6 || 7 => 'weekend',
    >= 1 && <= 5 => 'weekday',
    _ => 'invalid',
  };
  print(kind); // weekend
}

Hàm

Hàm là đối tượng hạng nhất. Tham số có thể là vị trí, vị trí tùy chọn trong [], hoặc có tên trong {}. Tham số có tên là tùy chọn trừ khi đánh dấu required. Cú pháp mũi tên => là cách viết tắt cho hàm trả về một biểu thức.

int add(int a, int b) => a + b;
String greet(String name, {String greeting = 'Hello', required bool loud}) {
  var text = '$greeting, $name';
  return loud ? text.toUpperCase() : text;
}
String label(String text, [int? width]) =>
    width == null ? text : text.padRight(width, '.');
void main() {
  print(add(2, 3));                              // 5
  print(greet('Ana', loud: false));              // Hello, Ana
  print(greet('Ana', greeting: 'Hi', loud: true)); // HI, ANA
  print(label('id', 5));                         // id...
  var twice = (int x) => x * 2;                  // anonymous function
  print([1, 2, 3].map(twice).toList());          // [2, 4, 6]
}

Collection: List, Set, Map và record

Các collection cốt lõi là List (có thứ tự), Set (phần tử duy nhất) và Map (cặp khóa-giá trị). Literal của collection hỗ trợ if, for và toán tử trải .... Record, thêm vào ở Dart 3, gom nhiều giá trị mà không cần khai báo lớp.

void main() {
  var nums = [3, 1, 2];               // List<int>
  nums.add(4);
  nums.sort();
  print(nums);                        // [1, 2, 3, 4]
  var tags = {'dart', 'flutter', 'dart'};
  print(tags.length);                 // 2
  var ages = {'Ann': 31, 'Bo': 25};   // Map<String, int>
  ages['Cy'] = 40;
  print(ages['Bo']);                  // 25
  var showAll = false;
  var menu = ['Home', if (showAll) 'Admin', for (var n in nums) 'Item $n'];
  print(menu);  // [Home, Item 1, Item 2, Item 3, Item 4]
  (String, int) pair = ('Ann', 31);   // a record
  var (who, age) = pair;              // destructuring
  print('$who is $age');              // Ann is 31
}

Lớp và async/await

Dart hướng đối tượng: mọi giá trị đều là đối tượng. Lớp hỗ trợ constructor với tham số khởi tạo (this.x), constructor có tên, getter, mixin và interface. Mã bất đồng bộ dùng Future và Stream với async và await, giúp ứng dụng Flutter luôn phản hồi khi chờ tệp hoặc mạng.

class Point {
  final double x, y;
  const Point(this.x, this.y);
  Point.origin() : x = 0, y = 0;     // named constructor
  double get sum => x + y;           // getter
  @override
  String toString() => 'Point($x, $y)';
}
Future<String> fetchUser() async {
  await Future.delayed(Duration(milliseconds: 100));
  return 'ana';
}
Future<void> main() async {
  print(Point(1, 2).sum);            // 3.0
  print(Point.origin());             // Point(0.0, 0.0)
  var user = await fetchUser();
  print('user: $user');              // user: ana
}

Xử lý lỗi

Ném ngoại lệ bằng throw và xử lý bằng try, on, catch và finally. Dùng on SomeType để bắt một kiểu cụ thể. Dart phân biệt Exception (vấn đề dự kiến mà chương trình có thể xử lý) với Error (lỗi lập trình, như assertion thất bại hoặc lỗi vượt phạm vi) mà thông thường bạn không nên bắt.

int parseAge(String s) {
  var n = int.tryParse(s);            // null instead of throwing
  if (n == null || n < 0) {
    throw FormatException('Not a valid age: $s');
  }
  return n;
}
void main() {
  for (var input in ['42', 'abc']) {
    try {
      print(parseAge(input));
    } on FormatException catch (e) {
      print('Error: ${e.message}');
    } finally {
      print('checked $input');
    }
  }
}
// 42 / checked 42 / Error: Not a valid age: abc / checked abc

Một chương trình hoàn chỉnh nhỏ: tổng tiền giỏ hàng

Chương trình này mô hình hóa sản phẩm bằng một lớp, lưu giỏ hàng trong một map và in hóa đơn. Nó kết hợp lớp, map, định dạng chuỗi và các phương thức của collection.

class Product {
  final String name;
  final double price;
  const Product(this.name, this.price);
}
void main() {
  const apple = Product('Apple', 0.5);
  const bread = Product('Bread', 2.25);
  final cart = <Product, int>{apple: 6, bread: 2};
  var total = 0.0;
  cart.forEach((product, qty) {
    final line = product.price * qty;
    total += line;
    print('${product.name.padRight(6)} x$qty  ${line.toStringAsFixed(2)}');
  });
  final count = cart.values.reduce((a, b) => a + b);
  print('$count items, total ${total.toStringAsFixed(2)}');
}
// Apple  x6  3.00
// Bread  x2  4.50
// 8 items, total 7.50

Học tiếp ở đâu

  • Language tour và các hướng dẫn về Dart tại dart.dev.
  • Effective Dart, hướng dẫn chính thức về phong cách và thiết kế.
  • Tài liệu Flutter tại flutter.dev nếu bạn muốn xây dựng ứng dụng.
  • pub.dev, kho gói của Dart và Flutter.

Câu hỏi thường gặp

Dart chỉ dùng cho Flutter thôi à?

Không. Dart là ngôn ngữ đa dụng và có thể dùng để viết công cụ dòng lệnh, máy chủ và ứng dụng web. Flutter chỉ đơn giản là lý do phổ biến nhất để học Dart.

Dart là ngôn ngữ biên dịch hay thông dịch?

Có cả hai chế độ. Khi phát triển, Dart chạy trên VM với biên dịch JIT (just-in-time) và hot reload; khi phát hành, nó được biên dịch AOT (ahead-of-time) ra mã máy native, hoặc ra JavaScript hay WebAssembly cho web.

final và const trong Dart khác nhau thế nào?

Biến final chỉ gán một lần, có thể ở lúc chạy. Giá trị const phải biết ngay lúc biên dịch và bất biến hoàn toàn, nhờ đó Flutter có thể tái sử dụng các widget hằng.

Dart có giống Java hay JavaScript không?

Cú pháp kiểu C và các lớp của Dart quen thuộc với lập trình viên Java, C# và JavaScript. Khác JavaScript, Dart có kiểu tĩnh và sound null safety.

Toán tử ?? trong Dart làm gì?

a ?? b trả về a nếu a không null, ngược lại trả về b. Toán tử liên quan a ??= b chỉ gán b cho a khi a là null.

Tất cả hướng dẫn lập trình