- Khai báo function với positional, optional positional, và named parameters.
- Hiểu
requiredkeyword cho named param. - Dùng arrow function syntax
=>cho function ngắn. - Function là first-class: gán biến, truyền argument, return function.
- Viết closure và hiểu capture biến.
- Dùng control flow đầy đủ:
if,switch(cũ + Dart 3 expression),for,while,break,continue, label. - Hiểu
assertvà khi nào dùng.
1. Function declaration cơ bản
Cú pháp chuẩn:
int add(int a, int b) {
return a + b;
}
void main() {
print(add(2, 3)); // 5
}
Phân tích:
- Return type (
int) — đứng trước tên.voidnếu không trả gì. - Tên function — camelCase idiomatic.
- Parameter list — mỗi param có type + tên.
- Body — trong
{ }, córeturnnếu return type khác void.
// JS
function add(a, b) { return a + b; }
const add2 = (a, b) => a + b;
// TS
function add(a: number, b: number): number { return a + b; }
// Dart — gần TS, return type đứng đầu
int add(int a, int b) => a + b;
2. Positional parameters
Default — param bắt buộc theo thứ tự. Caller phải truyền đủ và đúng order:
void greet(String firstName, String lastName) {
print('Xin chào $firstName $lastName');
}
greet('Việt', 'Nguyễn'); // Xin chào Việt Nguyễn
// greet('Việt'); // ❌ Thiếu lastName
// greet('Nguyễn', 'Việt'); // ✅ Compile OK nhưng SAI ý nghĩa
Function 5+ positional param khó đọc:
connectToServer('api.example.com', 443, true, 30, false);
true? false? 30? Đây là lúc cần named parameters.
3. Optional positional parameters
Wrap param trong [ ] để optional:
String greet(String name, [String? title]) {
if (title != null) return '$title $name';
return 'Xin chào $name';
}
greet('Việt'); // Xin chào Việt
greet('Việt', 'Anh'); // Anh Việt
Có thể có default value:
int add(int a, [int b = 0, int c = 0]) {
return a + b + c;
}
print(add(1)); // 1
print(add(1, 2)); // 3
print(add(1, 2, 3)); // 6
4. Named parameters + required
Đây là pattern bạn sẽ gặp ở MỌI widget Flutter. Wrap trong { }:
void connectToServer({
required String host,
int port = 8080,
bool secure = false,
Duration timeout = const Duration(seconds: 30),
}) {
print('Connect $host:$port secure=$secure timeout=$timeout');
}
// Gọi — chỉ truyền những gì cần, theo thứ tự bất kỳ
connectToServer(host: 'api.example.com');
connectToServer(host: 'api.example.com', port: 443, secure: true);
connectToServer(secure: true, host: 'api.example.com'); // thứ tự đảo OK
Quy tắc required:
- Named param nullable (
T?): mặc định optional, không cầnrequired. - Named param non-nullable không default: BẮT BUỘC
required. - Named param non-nullable có default: không cần
required(vì có default).
void demo({
String? a, // OK — nullable, optional
required String b, // OK — required keyword
String c = 'default', // OK — non-null nhưng có default
// String d, // ❌ Lỗi compile — non-null, no default, no required
}) {}
Widget Flutter thường có 10-30+ properties. Nếu positional thì caller phải nhớ thứ tự — không khả thi.
Named param + required = mọi widget chỉ rõ param nào bắt buộc, param nào optional, có default. Code đọc như JSON.
ElevatedButton(
onPressed: () => print('tap'),
style: ElevatedButton.styleFrom(...),
child: const Text('Submit'),
)
5. Arrow function — shorthand
Function 1 expression có thể viết gọn:
// Cách dài
int square(int x) {
return x * x;
}
// Cách ngắn — arrow
int square(int x) => x * x;
Note: chỉ 1 expression, không phải block:
// ❌ Sai — block trong arrow
// int square(int x) => { return x * x; };
// ✅ Đúng — expression
int square(int x) => x * x;
// ✅ Cũng OK — gọi method, in-line conditional
String grade(int score) => score > 50 ? 'pass' : 'fail';
6. Anonymous function / lambda
Function không tên — dùng inline:
// Truyền vào higher-order function
final doubled = [1, 2, 3].map((x) => x * 2).toList();
print(doubled); // [2, 4, 6]
// Hoặc block
[1, 2, 3].forEach((x) {
print('item: $x');
});
// Lưu vào biến
final isEven = (int n) => n % 2 == 0;
print(isEven(4)); // true
7. Function là first-class value
Dart treat function như object thường — có type, gán biến được, truyền argument, return từ function khác.
int square(int x) => x * x;
void main() {
// Gán function vào biến
var f = square; // type: int Function(int)
print(f(5)); // 25
// Truyền vào higher-order
final xs = [1, 2, 3].map(square).toList();
print(xs); // [1, 4, 9]
}
Function type — viết explicit
// Type của function
int Function(int) f = square;
bool Function(int) isEven = (n) => n % 2 == 0;
// typedef — alias cho function type
typedef IntTransformer = int Function(int);
IntTransformer addOne = (x) => x + 1;
IntTransformer mulTwo = (x) => x * 2;
// Higher-order: function nhận + return function
IntTransformer compose(IntTransformer f, IntTransformer g) {
return (x) => f(g(x));
}
final plus1Mul2 = compose(mulTwo, addOne);
print(plus1Mul2(3)); // (3+1)*2 = 8
8. Closure — function giữ scope ngoài
Closure là function lambda capture biến từ scope bên ngoài:
Function makeCounter() {
var count = 0;
return () {
count++;
return count;
};
}
final counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
print(counter()); // 3 — count được giữ giữa các lần gọi!
// Mỗi gọi makeCounter() tạo closure ĐỘC LẬP
final a = makeCounter();
final b = makeCounter();
print(a()); // 1
print(a()); // 2
print(b()); // 1 — riêng counter cho b
Pattern factory với closure:
int Function(int) multiplier(int factor) {
return (x) => x * factor; // capture factor
}
final double_ = multiplier(2);
final triple = multiplier(3);
print(double_(5)); // 10
print(triple(5)); // 15
Closure capture biến qua reference. Khi biến outer thay đổi, closure thấy giá trị mới. Ví dụ:
var x = 10;
final getX = () => x;
print(getX()); // 10
x = 20;
print(getX()); // 20 — không phải 10!
9. Control flow — if / else
Standard, chỉ điểm:
if (score >= 90) {
print('A');
} else if (score >= 80) {
print('B');
} else {
print('C');
}
// Ternary
final grade = score >= 50 ? 'pass' : 'fail';
// Lưu ý: điều kiện PHẢI là bool — không có truthy/falsy
// if (someList) { } // ❌
// if (someList.isNotEmpty) { } // ✅
10. Switch statement cổ điển
void describe(int day) {
switch (day) {
case 1:
print('Thứ Hai');
break;
case 2:
print('Thứ Ba');
break;
default:
print('Ngày khác');
}
}
Khác C/Java — mỗi case tự động break. break trong code trên thực ra không cần (analyzer cảnh báo).
Để fallthrough phải dùng continue label;.
11. Switch expression Dart 3
Cú pháp mới — switch trả value:
String describeStatus(int code) {
return switch (code) {
200 => 'OK',
201 => 'Created',
301 || 302 => 'Redirect',
404 => 'Not Found',
>= 500 => 'Server Error',
_ => 'Unknown',
};
}
print(describeStatus(200)); // OK
print(describeStatus(302)); // Redirect
print(describeStatus(503)); // Server Error
Features:
- Trả value — assignable.
=>arrow cho mỗi case.- Multiple values với
||. - Relational pattern:
>= 500,< 100. - Wildcard
_cho default. - Exhaustive check — analyzer ép cover hết case (đặc biệt với sealed class).
Đây mới là switch expression cơ bản. Chương 8 sẽ học destructuring, object pattern, sealed class + exhaustive switch.
12. For / for-in / forEach
// For index — quen từ C
for (var i = 0; i < 5; i++) {
print(i);
}
// For-in — iterate
final names = ['a', 'b', 'c'];
for (final name in names) {
print(name);
}
// forEach method — function-style
names.forEach(print);
names.forEach((n) => print('name: $n'));
// Map iterate
final ages = {'Việt': 25, 'Anh': 30};
for (final entry in ages.entries) {
print('${entry.key}: ${entry.value}');
}
forEach không break được
forEach là method, không phải statement. Không có break:
// ❌ Không work — break không hợp lệ ngoài loop
names.forEach((n) {
if (n == 'b') break; // Lỗi compile
});
// ✅ Dùng for-in nếu cần break
for (final n in names) {
if (n == 'b') break;
print(n);
}
13. While / do-while
var i = 0;
while (i < 5) {
print(i);
i++;
}
// do-while — body chạy ít nhất 1 lần
var j = 10;
do {
print(j);
j--;
} while (j > 5);
14. break, continue, label
// Vòng lồng — label để break từ vòng trong
outer: for (var i = 0; i < 5; i++) {
for (var j = 0; j < 5; j++) {
if (i == 2 && j == 3) {
break outer; // thoát cả 2 vòng
}
print('$i, $j');
}
}
// continue — skip đến iteration tiếp theo
for (var n = 0; n < 10; n++) {
if (n % 2 == 0) continue;
print(n); // chỉ in số lẻ
}
15. assert — debug check
assert kiểm tra điều kiện. Chỉ chạy trong debug/JIT mode. Production AOT build skip hoàn toàn.
int divide(int a, int b) {
assert(b != 0, 'Divisor must not be zero');
return a ~/ b;
}
divide(10, 2); // 5
divide(10, 0); // Debug: AssertionError. Production: phép chia 0 → exception khác.
assert ≠ runtime validation
Vì assert bị skip ở production, đừng dùng nó để validate input user. Use cases:
- Sanity check internal — invariant của module.
- Catch dev bug sớm (truyền null vào nơi không nên).
- Document expected condition (assert is documentation).
Validation input user phải dùng throw + exception thường (chương 5).
16. Bài tập
Named param + required
Viết function connectToServer({required String host, int port = 8080, bool secure = false}).
Gọi 3 cách khác nhau và in kết quả.
💡 Gợi ý đáp án
void connectToServer({
required String host,
int port = 8080,
bool secure = false,
}) {
final protocol = secure ? 'https' : 'http';
print('Connect $protocol://$host:$port');
}
void main() {
connectToServer(host: 'api.example.com');
connectToServer(host: 'api.example.com', port: 443, secure: true);
connectToServer(secure: true, host: 'api.example.com');
}
Multiplier với closure
Viết Function makeMultiplier(int factor) trả về function nhân factor.
Test với final double_ = makeMultiplier(2); double_(5) → 10.
💡 Gợi ý đáp án
int Function(int) makeMultiplier(int factor) {
return (x) => x * factor;
}
void main() {
final double_ = makeMultiplier(2);
final triple = makeMultiplier(3);
print(double_(5)); // 10
print(triple(5)); // 15
print(double_(10)); // 20
}
Note: type int Function(int) rõ hơn dùng Function generic — type-safe.
Switch expression mapping HTTP status
Cho list status code [200, 201, 301, 404, 500], dùng switch expression mapping ra
['ok', 'created', 'redirect', 'not found', 'error'].
💡 Gợi ý đáp án
String describe(int code) => switch (code) {
200 => 'ok',
201 => 'created',
300 || 301 || 302 => 'redirect',
404 => 'not found',
>= 500 => 'error',
_ => 'unknown',
};
void main() {
final codes = [200, 201, 301, 404, 500];
final descs = codes.map(describe).toList();
print(descs); // [ok, created, redirect, not found, error]
}
Closure bug fix
Cho đoạn code:
List<Function> makeAdders() {
final adders = <Function>[];
for (var i = 0; i < 3; i++) {
adders.add(() => i);
}
return adders;
}
void main() {
final fs = makeAdders();
for (final f in fs) {
print(f()); // mong đợi: 0, 1, 2
}
}
Output thực tế là gì? Fix nó để in 0, 1, 2.
💡 Gợi ý đáp án
Output: 0, 1, 2 — không phải bug! Dart capture biến by reference theo iteration của for.
Mỗi iteration có biến i mới (do var i trong for-init).
Khác JavaScript với var (function scope) — JS gặp bug này, print 3, 3, 3. Phải dùng let để fix.
Bài học: Dart for-loop scope đúng. Closure capture đúng biến iteration. Không cần fix.
Sum với for vs fold
Viết function lấy List<int> trả về sum. Implement 2 cách: (a) for-in loop manual, (b) dùng .fold.
So sánh code length và readability.
💡 Gợi ý đáp án
// (a) For-in
int sumLoop(List<int> xs) {
var total = 0;
for (final x in xs) {
total += x;
}
return total;
}
// (b) fold — function style
int sumFold(List<int> xs) => xs.fold(0, (a, b) => a + b);
// Hoặc dùng reduce nếu list không rỗng
int sumReduce(List<int> xs) => xs.reduce((a, b) => a + b);
void main() {
print(sumLoop([1, 2, 3, 4])); // 10
print(sumFold([1, 2, 3, 4])); // 10
print(sumReduce([1, 2, 3, 4])); // 10
}
.fold ngắn hơn nhưng phải biết signature. For-in dễ đọc cho beginner.
Chương 6 sẽ dùng .fold nhiều hơn.
17. Quiz
Named parameter nullable mặc định có cần required không?
Xem đáp án
Đáp án: Không. String? đã imply optional (có thể null). required chỉ cần khi non-nullable không có default.
Named parameter non-nullable không có default — cần gì?
Xem đáp án
Đáp án: required keyword. Nếu không có → lỗi compile vì biến phải có giá trị trước khi đọc (definite assignment).
Dart switch có fallthrough mặc định như C không?
Xem đáp án
Đáp án: Không. Mỗi case auto-break. Khác C/Java. Fallthrough phải explicit qua continue label;.
Arrow function => body có thể chứa nhiều statement không?
Xem đáp án
Đáp án: Không. Chỉ 1 expression. Cần nhiều statement → dùng { ... } block. Không thể (x) => { stmt1; stmt2; }.
void Function() và Function khác gì?
Xem đáp án
Đáp án: void Function() là type cụ thể — function không param, không return. Function là parent type generic — mất type safety, không biết signature.
Closure capture biến — khi biến outer thay đổi, closure thấy giá trị mới hay cũ?
Xem đáp án
Đáp án: Mới. Closure capture by reference, không by value. Đổi outer → closure thấy ngay.
for (var i = 0; i < 3; i++) vs [0,1,2].forEach((i) {...}) — cái nào break được giữa chừng?
Xem đáp án
Đáp án: for được. forEach không — vì là method, body là function bình thường, không có loop context. Cần break → dùng for-in hoặc xs.takeWhile(...) kiểu functional.
assert(condition) chạy trong production AOT build không?
Xem đáp án
Đáp án: Không. Chỉ chạy debug/JIT mode (dart run, Flutter debug). Release AOT skip hoàn toàn — performance optimization. Đừng dùng assert cho validation user input.
18. Tổng kết
- ✅ Function declaration: return type + name + params + body.
- ✅ 3 loại parameter: positional (bắt buộc theo thứ tự), optional positional
[ ], named{ }. - ✅
requiredkeyword cho named param non-nullable không default. - ✅ Arrow
=>cho function 1-expression. - ✅ Function là first-class: gán biến, truyền argument, return từ function.
- ✅ Closure capture by reference scope ngoài.
- ✅ Switch expression Dart 3 — exhaustive, trả value.
- ✅ Loop:
for,for-in,while,do-while, label break. - ✅
assertchỉ debug-only.
Pattern bạn sẽ gặp ở Flutter mỗi chương:
Widget build(BuildContext context) {
return Column(
children: [
Text('Hello'),
ElevatedButton(
onPressed: () => print('tap'), // lambda
child: const Text('Submit'),
),
],
);
}
Mọi widget constructor dùng named param + required ngầm. onPressed nhận function (callback). Children là List của widget. — pattern này bạn đã quen sau chương 3.
19. Kết nối tới các chương khác
- Chương 4 — Classes: constructor là function đặc biệt + named param +
required. - Chương 7 — Async: async function là dạng function với keyword
async. - Chương 8 — Patterns: switch expression mở rộng với pattern matching.
- Flutter từ chương 2: mọi widget constructor dùng
{required ...}.