Hướng Nghiệp Dữ Liệu

Dart cho người mới: 10 khái niệm cần nắm trước khi viết app Flutter

Đăng bởi Admin
Dart cho người mới: 10 khái niệm cần nắm trước khi viết app Flutter

Minh hoạ 10 khái niệm Dart cần nắm trước khi viết app Flutter

Flutter dùng Dart làm ngôn ngữ duy nhất. Điều này có nghĩa: mọi widget bạn viết, mọi dòng logic bạn xử lý, mọi lỗi bạn gặp khi làm app — cuối cùng đều là câu hỏi về Dart. Người mới thường muốn "nhảy thẳng" vào Flutter vì thấy giao diện đẹp, nhưng chỉ vài buổi là kẹt ở những chỗ như null check operator used on a null value, Future chưa await, hay không hiểu vì sao setState không cập nhật giao diện.

Bài này là bài thứ hai trong series 4 bài về Flutter. Bài trước đã trả lời "có nên học Flutter" và vạch lộ trình 5 giai đoạn; bài này đi sâu vào giai đoạn 1 của lộ trình đó: nền tảng Dart.

Mục tiêu rất cụ thể: sau khi đọc xong và tự tay chạy lại các ví dụ, bạn nắm được 10 khái niệm Dart đủ để đọc hiểu mọi đoạn code Flutter cơ bản — thay vì copy-paste rồi cầu mong nó chạy.

Vì sao phải học Dart trước, không học song song?

Câu trả lời ngắn: vì Flutter không che giấu Dart.

Nếu bạn học React Native, phần lớn thời gian bạn làm việc với JavaScript/TypeScript mà bạn có thể đã biết. Với Flutter, Dart là ngôn ngữ hoàn toàn mới với phần lớn người mới. Khi bạn viết:

[object Object]

Đoạn code này chứa ít nhất 6 khái niệm Dart: class, kế thừa (extends), constructor có tên, constructor const, override, và kiểu trả về. Nếu bạn chưa nắm những khái niệm đó, bạn chỉ đang gõ lại ký tự mà người khác viết.

Ngược lại, khi đã nắm Dart, việc học Flutter nhanh hơn hẳn — vì lúc đó bạn chỉ còn phải học một thứ mới: hệ thống widget, chứ không phải hai thứ cùng lúc.

Cách học hiệu quả: đừng đọc hết tài liệu Dart rồi mới làm Flutter. Hãy học 10 khái niệm trong bài này, làm vài bài tập nhỏ trên DartPad, rồi bắt đầu Flutter và quay lại tra cứu Dart khi cần. Vòng lặp "học → làm → tra cứu" luôn nhanh hơn "học hết → làm".

Khái niệm 1: Biến, kiểu dữ liệu và từ khóa khai báo

Dart là ngôn ngữ statically typed nhưng có type inference — nghĩa là bạn có thể khai báo kiểu, hoặc để Dart tự suy ra.

[object Object]

Các điểm cần nhớ:

  • var: Dart tự suy kiểu từ giá trị đầu tiên. Sau đó bạn không thể gán giá trị khác kiểu. var x = 1; x = 'abc'; sẽ báo lỗi.
  • final: gán một lần rồi không được thay đổi nữa. final được đánh giá lúc chạy (runtime) — ví dụ final now = DateTime.now(); hợp lệ.
  • const: hằng số được đánh giá lúc biên dịch (compile-time). const x = 5; hợp lệ, const now = DateTime.now(); không hợp lệ. const giúp app nhanh hơn vì Dart loại bỏ việc tạo lại đối tượng — đây là lý do bạn thấy rất nhiều const trong code Flutter.
  • dynamic: bỏ kiểm tra kiểu. Chỉ dùng khi bắt buộc (ví dụ dữ liệu JSON chưa biết hình dạng). Lạm dụng dynamic là nguyên nhân số một của lỗi chỉ xuất hiện khi chạy thật.

Lỗi người mới hay gặp: khai báo var rồi gán lại giá trị khác kiểu, hoặc dùng const với giá trị chỉ biết lúc chạy. Hãy nhớ quy tắc: mặc định final, dùng const khi có thể, hạn chế var, tránh dynamic.

Khái niệm 2: Null safety — phần quan trọng nhất

Dart có null safety: biến bình thường không được phép null.

[object Object]

Ba toán tử bạn sẽ gặp mỗi ngày:

[object Object]

Ngoài ra còn late cho biến sẽ được gán sau khi khai báo:

[object Object]

Vì sao phần này quan trọng với Flutter: rất nhiều widget nhận tham số có thể null (TextEditingController?, VoidCallback?, String? errorText). Khi đọc code Flutter, bạn sẽ thấy ?! khắp nơi. Hiểu null safety giúp bạn đọc được code, và quan trọng hơn là tránh làm app crash.

Quy tắc vàng: ! là lời hứa với trình biên dịch. Chỉ dùng khi bạn thật sự chắc chắn. Gặp lỗi Null check operator used on a null value — nghĩa là lời hứa đó sai, và cách sửa thường không phải là thêm ! mà là xử lý nhánh null bằng ?? hoặc if (x != null).

Khái niệm 3: Hàm, tham số và closures

Dart coi hàm là đối tượng hạng nhất (first-class object) — hàm có thể truyền như tham số, trả về từ hàm khác.

[object Object]

Điểm cần nhớ: named parameters là kiểu tham số bạn gặp nhiều nhất khi đọc code Flutter (required, giá trị mặc định). Constructor của widget cũng dùng chính cơ chế này, nên hiểu hàm = hiểu widget.

Khái niệm 4: Collections — List, Set, Map và các thao tác chuỗi

Ba kiểu dữ liệu bạn dùng hằng ngày:

[object Object]

Trong Flutter, bạn càng gặp nhiều cấu trúc lồng nhau (ví dụ danh sách tin nhắn trong từng cuộc hội thoại) nên các hàm map, where, fold, any, every, firstWhere sẽ xuất hiện liên tục. Nắm chúng giúp bạn biến dữ liệu API thành giao diện nhanh gọn.

Lỗi hay gặp: sửa danh sách trong lúc đang duyệt (ConcurrentModificationError), hoặc quên .toList() nên nhận về Iterable rồi không dùng được các hàm của List. Ghi nhớ: các hàm như where/map trả Iterablelazy, muốn dùng ngay thì .toList().

Khái niệm 5: Class, constructor, field và method

Flutter là widget-based, và widget là class. Đây là khái niệm bạn gặp ở 100% file Flutter.

[object Object]

Hai điều cốt lõi luôn gặp trong Flutter:

  1. required this.x — cách viết tắt của "tham số có tên, gán thẳng vào field". Bạn thấy nó ở mọi widget.
  2. @override + build() — widget StatelessWidgetStatefulWidget bắt bạn override build. Đây là lý do đầu tiên bạn phải hiểu class, kế thừa trong Dart.

Vì sao Flutter chuộng đối tượng bất biến (immutable) và copyWith

Trong ví dụ trên, NguoiDung không có setter — muốn "đổi tuổi" bạn tạo đối tượng mới. Đây không phải ngẫu nhiên mà là chủ ý của Flutter: widget là bất biến.

Lý do rất thực tế: Flutter so sánh widget cũ và widget mới để quyết định có cần vẽ lại không. Nếu widget có thể bị sửa ở bất kỳ đâu, việc so sánh mất ý nghĩa và app dễ sinh bug "giao diện không cập nhật". Vì vậy bạn sẽ gặp liên tục mẫu:

[object Object]

Thói quen nên tập ngay: field final, tạo đối tượng mới thay vì sửa tại chỗ. Đây là gốc rễ của cách làm việc với mọi thư viện quản lý trạng thái trong Flutter.

Khái niệm 6: Kế thừa, abstract và mixin

Ba cách tái sử dụng code trong Dart:

[object Object]

Trong Flutter bạn đã gặp cả ba: extends StatelessWidget, implements TickerProvider, with SingleTickerProviderStateMixin. Hiểu sự khác nhau giúp bạn không bối rối khi phải chọn cái nào — quy tắc đơn giản: extends khi là quan hệ "là một loại", mixin khi muốn thêm khả năng, và chỉ dùng implements khi bạn muốn tuân theo một interface.

Khái niệm 7: Enum, records và pattern matching (Dart 3)

Dart 3 mang đến switch mạnh hơn nhiều so với trước đây.

[object Object]

Vì sao cần biết: code Flutter hiện đại dùng switch expression cho trạng thái (loading / success / error) rất nhiều. Đây cũng là cách viết gọn, ít lỗi hơn chuỗi if-else dài.

Khái niệm 8: Async, Future và Stream

Đây là khái niệm quyết định bạn có làm được app gọi API hay không.

[object Object]

Quy tắc dễ nhớ:

  • async đánh dấu hàm bất đồng bộ; hàm async luôn trả về Future.
  • await = "dừng ở đây chờ xong rồi chạy tiếp", chỉ dùng được trong hàm async.
  • Không await thì bạn nhận về Future chứ không phải giá trị — đây là lỗi phổ biến nhất khi mới làm việc với API.

Về Stream: khi dữ liệu đến nhiều lần theo thời gian (socket, chat, vị trí) thì dùng Stream thay vì Future.

[object Object]

Lỗi hay gặp: gọi API, set state trước khi có dữ liệu; hoặc quên xử lý lỗi mạng khiến app hiện màn hình trắng. Nên luôn bọc try/catch và luôn có trạng thái báo cho người dùng: đang tải, thành công, lỗi.

Nơi async gặp giao diện: FutureBuilderStreamBuilder

Hiểu Future là điều kiện để hiểu hai widget quan trọng nhất khi làm app có dữ liệu thật:

[object Object]

Ba sai lầm kinh điển khi mới dùng FutureBuilder:

  1. Gọi API trực tiếp trong build() (future: layDuLieu()) → mỗi lần vẽ lại là một lần gọi mạng. Hãy tạo Future một lần trong initState rồi truyền vào.
  2. Quên xử lý hasError → app sập hoặc trắng màn hình khi mất mạng.
  3. Truyền Future mới mỗi lần rebuild → giao diện nhấp nháy liên tục.

Với dữ liệu đến nhiều lần (chat, tỷ giá, tiến trình tải), bạn dùng StreamBuilder cùng nguyên tắc: xử lý connectionState, lỗi, và dữ liệu rỗng trước khi render.

Khái niệm 9: Generics — vì sao bạn thấy <T> khắp nơi

[object Object]

Generics giúp code an toàn kiểu và tái sử dụng. Trong Flutter bạn gặp liên tục: Future<List<SanPham>>, ValueNotifier<int>, List<Widget>, Provider.of<GioHang>(context). Đọc được generics nghĩa là bạn biết dữ liệu đang chảy qua app có hình dạng gì — kỹ năng bắt buộc khi làm việc với API.

Khái niệm 10: Xử lý lỗi và kiểm tra dữ liệu vào

[object Object]

Người mới thường để lỗi "nổi lên" rồi app crash hoặc hiện màn hình đỏ. Ba thói quen nên tập từ đầu:

  1. Kiểm tra dữ liệu vào ngay tại biên (form, API response) thay vì rải if khắp nơi.
  2. Bắt lỗi có mục đích — bắt loại lỗi cụ thể (on FormatException) để xử lý đúng.
  3. Đừng bao giờ để người dùng thấy lỗi thô. Hiển thị thông báo hiểu được, còn chi tiết kỹ thuật chỉ ghi log.

7 lỗi Dart người mới hay gặp nhất và cách sửa

Đây là các lỗi bạn sẽ gặp trong 2 tuần đầu, kèm cách sửa đúng:

1. Null check operator used on a null value
Nguyên nhân: bạn dùng ! trên giá trị đang null. Cách sửa không phải là thêm ! mà là xử lý nhánh null:

[object Object]

2. Quên await khi gọi API
Bạn nhận về Future<List<...>> thay vì List<...>, sau đó gọi .length báo lỗi hoặc in ra Instance of 'Future'. Cách sửa: await trong hàm async, và nhớ hàm gọi nó cũng phải async.

3. setState() called after dispose()
Gọi API xong nhưng người dùng đã rời màn hình. Cách sửa: kiểm tra trước khi set state:

[object Object]

4. Sửa list trong lúc đang duyệt → ConcurrentModificationError
Không xoá phần tử trực tiếp trong for. Cách sửa: duyệt trên bản sao [...ds], hoặc dùng ds.where(...).toList().

5. type 'int' is not a subtype of type 'double'
Thường đến từ JSON: server trả 2 nhưng model khai báo double. Cách sửa: chuẩn hoá khi parse, ví dụ (json['gia'] as num).toDouble().

6. const với giá trị không phải hằng
const x = DateTime.now(); không hợp lệ. Dùng final cho giá trị chỉ biết lúc chạy.

7. Lạm dụng dynamic
Code chạy được lúc thử nhưng lỗi khi gặp dữ liệu khác. Cách sửa: khai báo model có kiểu rõ ràng (class SanPham { final String ten; ... }) và parse JSON thành model, thay vì dùng Map<String, dynamic> khắp nơi.

Luyện tập: 5 bài tập Dart nhỏ nên làm trước khi học Flutter

Lý thuyết không đủ. Làm 5 bài này trên DartPad — mỗi bài 15–30 phút:

  1. Máy tính điểm trung bình: nhập danh sách điểm, tính trung bình, in xếp loại. Luyện: List, hàm, fold.
  2. Quản lý danh bạ bằng Map: thêm, tìm theo tên, xoá. Luyện: Map, null safety (?, ??).
  3. Mô phỏng gọi API: viết hàm async trả về Future<List<String>> sau 1 giây, bắt lỗi khi rỗng. Luyện: Future, try/catch, async/await.
  4. Class sản phẩm: có final field, constructor required, method giảmGiá(). Luyện: class, immutable, const.
  5. Xử lý chuỗi trạng thái bằng switch expression cho 4 trạng thái đơn hàng. Luyện: enum, pattern matching.

Làm hết 5 bài này, bạn sẽ đọc code Flutter cơ bản mà không bị chặn ở phần ngôn ngữ nữa.

Bản đồ: khái niệm Dart này bạn gặp ở đâu trong Flutter

Bảng dưới đây giúp bạn thấy ngay mối liên hệ — khi đọc code Flutter, bạn sẽ nhận ra mình đang nhìn khái niệm nào:

Khái niệm Dart Bạn gặp ở đâu trong Flutter
final / const Constructor widget (const Text('...')), hằng số màu, style dùng chung
Null safety (?, ??) Tham số widget có thể bỏ trống, model từ API, xử lý dữ liệu thiếu
Named parameters Mọi widget: Text('x', style: ..., textAlign: ...)
List / Map ListView.builder, dựng danh mục, map dữ liệu JSON → model
Class + extends class HomePage extends StatelessWidget
@override + build() Bắt buộc với mọi widget bạn tự viết
Mixin with SingleTickerProviderStateMixin khi làm animation
Enum + switch Trạng thái màn hình (loading / success / error), trạng thái đơn hàng
Future + async/await Gọi API, đọc/ghi dữ liệu cục bộ, chờ quyền truy cập
FutureBuilder / StreamBuilder Màn hình có dữ liệu từ mạng, danh sách chat
Generics <T> Future<List<SanPham>>, ValueNotifier<int>, Provider.of<GioHang>
Xử lý lỗi Bắt lỗi mạng, validate form, hiển thị thông báo

Khi đã nhìn bảng này và thấy quen, bạn đã sẵn sàng học Widget ở bài 3.

Kết luận và bước tiếp theo

10 khái niệm trên không cần học thuộc. Điều bạn cần là nhận diện chúng khi đọc code, và biết tra cứu khi viết. Với nền Dart này, bạn có thể bắt đầu giai đoạn 2 của lộ trình: widget và giao diện.

Ba việc nên làm ngay sau bài này:

  1. Làm 5 bài tập Dart ở trên — đừng bỏ qua, đây là phần tạo khác biệt.
  2. Đọc lại bài mở đầu series nếu bạn chưa chốt lộ trình: Có nên học Flutter 2026? Lộ trình khóa học lập trình Flutter.
  3. Cài đặt môi trường theo hướng dẫn Cài đặt Flutter trên Windows, macOS, Linux chi tiết từng bước để sẵn sàng cho bài 3.

Bài 3 của series sẽ trả lời: Widget trong Flutter là gì? Hiểu StatelessWidget và StatefulWidget — trong đó mọi khái niệm Dart ở bài này sẽ được dùng lại ngay. Nếu muốn học có người sửa bài và làm dự án thật, bạn có thể xem khóa học Flutter thực chiến hoặc lịch khai giảng.

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

Học Dart mất bao lâu?

Nếu mỗi ngày 1–2 giờ và có làm bài tập, khoảng 1–2 tuần là đủ để đọc hiểu code Dart cơ bản và bắt đầu Flutter. Phần async và Stream thường cần thêm thời gian vì phải gặp tình huống thực tế mới thấm.

Có cần học hết Dart rồi mới làm Flutter không?

Không. Học 10 khái niệm cốt lõi trong bài này, làm bài tập, rồi bắt đầu Flutter. Những phần nâng cao (isolate, FFI, thư viện nâng cao) học sau khi đã có app chạy được.

Dart có khó hơn JavaScript không?

Với người đã biết JavaScript hoặc TypeScript, Dart khá quen: cú pháp gần giống, có class và async/await. Điểm mới chính là null safetykiểu tĩnh — hai thứ này ban đầu gây cảm giác "khắt khe" nhưng lại giúp giảm lỗi khi app lớn.

Không biết lập trình có học Dart được không?

Được, nhưng nên bắt đầu từ tư duy lập trình cơ bản (biến, điều kiện, vòng lặp, hàm). 10 khái niệm trong bài này chính là phần nền bạn cần, chỉ khác là phải chạy code nhiều hơn thay vì đọc.

Nên dùng DartPad hay cài Flutter để luyện Dart?

Giai đoạn học khái niệm nên dùng DartPad — không cần cài gì, chạy được ngay. Khi chuyển sang Flutter mới cần cài SDK.

constfinal khác nhau chỗ nào, khi nào dùng cái nào?

final gán một lần, giá trị được xác định lúc chạy — ví dụ final now = DateTime.now();. const là hằng số biết trước lúc biên dịch — ví dụ const pi = 3.14;, const Text('Xin chào'). Quy tắc: dùng const khi có thể (app nhanh hơn, ít vẽ lại), còn lại dùng final. Hạn chế var, và tránh dynamic trừ trường hợp bắt buộc.

Dart có phải là ngôn ngữ hướng đối tượng? Có cần biết OOP trước không?

Dart là ngôn ngữ hướng đối tượng — mọi thứ, kể cả hàm và số, đều là đối tượng. Bạn không cần học OOP hàn lâm trước, nhưng cần nắm 4 thứ: class, field, method, kế thừa. Trong bài này, khái niệm 5, 6 và 9 chính là phần OOP tối thiểu đủ dùng cho Flutter.

Bao giờ thì nên chuyển từ học Dart sang làm dự án thật?

Khi bạn làm xong 5 bài tập ở trên và đọc hiểu một file widget StatefulWidget bất kỳ mà không cần tra cứu cú pháp. Lúc đó hãy làm ngay một app nhỏ có 3–4 màn hình và gọi 1 API thật — làm dự án là cách học nhanh nhất, nhanh hơn nhiều so với xem thêm video.

Series 4 bài học Flutter cho người mới

  1. Có nên học Flutter năm 2026? Lộ trình khóa học lập trình Flutter
  2. Dart cho người mới: 10 khái niệm cần nắm trước khi viết app Flutter (bài này)
  3. Widget trong Flutter là gì? Hiểu StatelessWidget và StatefulWidget
  4. Tích hợp Gemini AI vào app Flutter bằng ví dụ thực tế