Dart
ภาษาโปรแกรมมิ่งที่สร้างโดย Google เป็นภาษาหลักที่ใช้เขียน Flutter
จุดเด่น
- รองรับทั้ง AOT (Ahead of Time) และ JIT (Just in Time) compilation
- Null safety — ช่วยลด bug เรื่อง null
- Async/await — จัดการ asynchronous ได้สะดวก
- Strong typing แต่มี type inference
- ทุกค่าเป็น Object (เหมือน Java)
Compile ทำงานยังไง
โค้ด Dart (.dart)
↓ Parse (อ่านจากบนลงล่าง)
↓ Compile (แปลง)
Native Machine Code ← ARM code สำหรับ iOS/Android
↓
ติดตั้ง + รันบน device
Dart ไม่ได้ copy โค้ดไปรันบน device ตรงๆ — โค้ดถูก parse → compile → กลายเป็น native machine code → ผลลัพธ์เป็น native app จริงๆ
ระบบ Type
Dart เป็นภาษา type-safe — ทุกค่าต้องมี type กำกับ และค่าหนึ่งมี หลาย type ได้:
'Hello World!' → เป็นทั้ง String และ Object
29 → เป็นทั้ง int, num และ Object
MaterialApp() → เป็นทั้ง MaterialApp, Widget และ ObjectCore Types
| Type | คืออะไร | ตัวอย่าง |
|---|---|---|
int | จำนวนเต็ม | 29, -15 |
double | ทศนิยม | 3.91, -12.81 |
num | เต็มหรือทศนิยม | 15, 15.01 |
String | ข้อความ | 'Hello World' |
bool | จริง/เท็จ | true, false |
Object | type พื้นฐานของทุกค่า | อะไรก็ได้ |
Generic Types
List<String> hobbies = ['Cooking', 'Sports'];
List<Color> colors = [Colors.green, Colors.red];Nullable Type
Alignment startAlignment; // ❌ error — ยังไม่มีค่า
Alignment? startAlignment; // ✅ ได้ — เพิ่ม ? บอกว่า null ก็ได้! (bang operator) — บอก Dart ว่า “ค่านี้ไม่เป็น null แน่นอน”
DateTime? _selectedDate;
// ❌ error — _selectedDate อาจเป็น null
formatter.format(_selectedDate);
// ✅ ใช้ ! บอกว่าไม่ null (ต้องมั่นใจเอง เช่น check null แล้ว)
if (_selectedDate != null) {
formatter.format(_selectedDate!);
}ตัวแปร: var, final, const
var | final | const | |
|---|---|---|---|
| เปลี่ยนค่าได้ไหม | ได้ | ไม่ได้ | ไม่ได้ |
| รู้ค่าตอนไหน | ตอนรัน | ตอนรัน | ตอน compile |
| เทียบ Java | ตัวแปรธรรมดา | final | static final + compile-time constant |
var x = Alignment.topLeft; // เปลี่ยนค่าได้
final y = DateTime.now(); // ล็อคค่า แต่รู้ตอนรัน
const z = Alignment.topLeft; // ล็อคค่า + รู้ตั้งแต่ compileกฎ: const > final > var — ใช้ตัวที่เข้มงวดที่สุดเท่าที่ทำได้
Reassign vs Mutate — ภาพรวม
| เปลี่ยน address (reassign) | แก้ object เดิม (mutate) | |
|---|---|---|
var | ✅ ได้ | ✅ ได้ |
final | ❌ ไม่ได้ | ✅ ได้ |
const | ❌ ไม่ได้ | ❌ ไม่ได้ |
final — ห้าม reassign แต่ mutate ข้างในได้
┌─── Code ───────────────┐ ┌─── Device Memory ──────────┐
│ │ │ │
│ final numbers = [1,2,3]│──────▶ <0x21d36e0> [1, 2, 3] │
│ │ │ ▲ │
│ numbers = [4,5,6] ❌ │──X──▶│ ❌ สร้าง object ใหม่ │
│ (reassign ไม่ได้) │ │ แล้ว assign ไม่ได้ │
│ │ │ │
│ numbers.shuffle() ✅ │──────▶ <0x21d36e0> [3, 1, 2] │
│ (แก้ข้างใน object ได้) │ │ ✅ แก้ data ใน object เดิม │
└────────────────────────┘ └────────────────────────────┘
finalล็อคแค่ ตัวแปรชี้ไปที่ไหน (ห้าม reassign)- แต่ ข้อมูลข้างใน object ที่ตัวแปรชี้อยู่ยังแก้ได้ (mutate)
shuffle()ไม่ได้สร้าง list ใหม่ แค่สลับของใน list เดิม → จึงไม่ขัดกับfinal
const — ห้ามทั้ง reassign และ mutate + ประหยัด Memory
const nums = [1, 2, 3];
nums = [4, 5]; // ❌ ห้าม reassign
nums.add(4); // ❌ ห้าม mutate — Unmodifiable list errorconst Text('Hello') // ใช้ที่ 1 → สร้างใน memory
const Text('Hello') // ใช้ที่ 2 → ชี้ไปที่เดิม ไม่สร้างใหม่constบอก Dart ว่าค่านี้จะไม่มีวันเปลี่ยน → cache ใน memory ก้อนเดียว ใช้ซ้ำได้- ต่างจาก
finalตรงที่ ห้าม mutate ข้างในด้วย (final แค่ห้าม reassign) - เขียนได้ 2 แบบ:
const nums = [1,2,3]หรือfinal nums = const [1,2,3] - IDE แนะนำให้ใส่
constทุกที่ที่ทำได้ (blue squiggly lines) - ถ้า widget ข้างในมี variable ที่เปลี่ยนได้ → ใส่
constไม่ได้
Garbage Collection
เมื่อ reassign ตัวแปรไปชี้ object ใหม่ → object เก่าที่ไม่มีใครชี้ถึงแล้ว Dart จะ ลบออกจาก memory อัตโนมัติ
var users = ['Max']; // object A ใน memory
users = ['Max', 'Manu']; // object B ใหม่ → object A ถูก garbage-collectList.of() — Copy list ก่อน mutate
final original = [3, 1, 2];
final copy = List.of(original); // copy ไม่แตะต้นฉบับ
copy.sort(); // sort เฉพาะ copyใช้เมื่อต้อง sort(), shuffle() หรือ method ที่ mutate list — ถ้าไม่ copy จะแก้ต้นฉบับ
Enum — ค่าคงที่แบบมีชื่อ
enum Category { food, travel, leisure, work }
// ใช้งาน:
var selected = Category.food;
Category.values // → list ของทุกค่า [food, travel, leisure, work]
selected.name // → 'food' (String)เหมาะกับค่าที่มีตัวเลือกจำกัด เช่น หมวดหมู่, สถานะ, ประเภท
Functions
Positional vs Named Arguments
// Positional — จับคู่ตามลำดับ
void add(a, b) { print(a + b); }
add(5, 10); // a=5, b=10
// Named — จับคู่ตามชื่อ (Flutter ใช้เป็นหลัก)
void add({required a, required b}) { print(a + b); }
add(b: 5, a: 10); // สลับลำดับได้| Positional | Named | |
|---|---|---|
| ลำดับ | สำคัญ | ไม่สำคัญ (ใช้ชื่อ) |
| Default | บังคับส่ง | optional (ไม่ส่งก็ได้) |
| ทำให้บังคับ | — | เพิ่ม required |
| ทำให้ optional | ห่อด้วย [ ] | default อยู่แล้ว |
| ใช้บ่อยใน | function ทั่วไป | widget constructor |
Functions as Values
ใน Dart function เป็น object — ส่งเป็น argument ได้:
TextButton(
onPressed: rollDice, // ← ส่ง function ไม่ใส่ () → รอ widget เรียก
child: const Text('Roll Dice'),
)
// กำหนดชนิด function ที่รับ
final void Function() startQuiz;
final void Function(String answer) onSelectAnswer;เทียบ Java: คล้าย method reference this::rollDice
Anonymous Functions (Lambda)
// ส่ง anonymous function ตรงๆ
onPressed: () { answerQuestion(answer); }
// ใช้ใน map
answers.map((answer) => AnswerButton(text: answer))Arrow Function =>
ถ้า function body มีแค่ return อย่างเดียว → ใช้ => แทนได้ สั้นกว่า:
// แบบปกติ
summaryData.where((data) {
return data['user_answer'] == data['correct_answer'];
})
// arrow function (ผลลัพธ์เหมือนกัน)
summaryData.where((data) => data['user_answer'] == data['correct_answer'])ลบ {}, return, ; ออก แล้วใส่ => แทน — ใช้ได้ทั้ง anonymous function และ method ปกติ
String Interpolation
var roll = 4;
var path = 'assets/images/dice-$roll.png'; // ตัวแปรเดี่ยว
var msg = 'Total: ${a + b}'; // expressionJava ต้องต่อ String ด้วย + แต่ Dart ใช้ $ สะอาดกว่า
Classes
Dart เป็นภาษา OOP — ทุกค่าเป็น Object สร้างจาก Class
class StyledText extends StatelessWidget {
const StyledText(this.text, {super.key}); // constructor + super
final String text; // instance variable
@override
Widget build(BuildContext context) {
return Text(text, style: const TextStyle(fontSize: 28));
}
}super.key คืออะไร?
{super.key} ส่ง Key? ไปให้ parent class (StatelessWidget / StatefulWidget) — ทุก Widget ใน Flutter มี parameter key อยู่แล้ว
class MyWidget extends StatelessWidget {
const MyWidget({super.key}); // ส่ง key ขึ้นไปให้ Widget parent
// เขียนเต็มคือ: const MyWidget({Key? key}) : super(key: key);
}ทำไมต้องมี?
- Flutter ใช้ key เพื่อระบุตัวตน ของ widget ใน widget tree ตอนทำ diffing (rebuild)
- ปกติ Flutter แยก widget ด้วย type + ตำแหน่ง — แต่ถ้า list เปลี่ยนลำดับ หรือ insert/remove item กลาง list จะ diff ผิด
- ใส่
Key(เช่นValueKey,UniqueKey) บอก Flutter ว่า widget ไหนคือตัวเดิม → rebuild ถูกต้อง - ใส่
{super.key}ใน constructor เสมอ เพื่อให้ผู้ใช้ widget สามารถส่ง key เข้ามาได้ถ้าต้องการ
Named Constructors
class หนึ่งมี constructor ได้หลายตัว:
GradientContainer(Colors.red, Colors.blue) // constructor ปกติ
GradientContainer.purple() // named constructor
Image.asset('path.png') // Flutter ใช้หลักการเดียวกันเทียบ Java: คล้าย static factory method เช่น List.of(), Optional.empty()
Private ใน Dart
ใช้ _ นำหน้าชื่อ (ไม่มี keyword private เหมือน Java) — ใช้ได้กับทั้ง class, property, และ method:
class _QuizState extends State<Quiz> { // private class
List<String> _selectedAnswers = []; // private property
void _switchScreen() { ... } // private method
}_ = ใช้ได้เฉพาะในไฟล์นี้ — ไฟล์อื่น import ไปก็เข้าถึงไม่ได้
Getter
class QuizQuestion {
const QuizQuestion(this.text, this.answers);
final String text;
final List<String> answers;
List<String> get shuffledAnswers { // getter — เรียกเหมือน property
final shuffled = List.of(answers);
shuffled.shuffle();
return shuffled;
}
}
// ใช้: question.shuffledAnswers (ไม่ต้องใส่ ())Initializer List — กำหนดค่า property ตอนสร้าง object
ใช้ : หลังวงเล็บ constructor เพื่อ assign ค่าให้ property ก่อน body ของ constructor ทำงาน
class Expense {
Expense({required this.title, required this.amount}) : id = uuid.v4();
// ↑ initializer list
final String id; // ไม่รับจากภายนอก — สร้างเองด้วย [[UUID]]
final String title;
final double amount;
}Collections
List
List<String> items = ['A', 'B', 'C'];
items.add('D');
items.length;
// map — แปลง list เป็น list ใหม่
items.map((item) => item.toUpperCase()).toList();
// where — filter (return Iterable เหมือน map)
items.where((item) => item != 'A').toList();
// ตัวอย่าง: นับจำนวนข้อที่ตอบถูกจาก summary data
final summaryData = [
{'question': 'What is Flutter?', 'user_answer': 'A UI toolkit', 'correct_answer': 'A UI toolkit'},
{'question': 'What is Dart?', 'user_answer': 'A language', 'correct_answer': 'A language'},
{'question': 'What is a Widget?', 'user_answer': 'A component', 'correct_answer': 'A building block'},
];
final numCorrect = summaryData.where((data) {
return data['user_answer'] == data['correct_answer'];
}).length; // → 2 (ไม่ต้อง toList() ถ้าแค่เอา .length)
// List.of — copy list
final copy = List.of(items);
copy.shuffle();| Method | แก้ list เดิมไหม | return |
|---|---|---|
map() | ไม่แก้ — สร้าง list ใหม่ | Iterable ใหม่ |
shuffle() | แก้ list เดิม (in place) | void |
ถ้าต้อง shuffle โดยไม่แก้ต้นฉบับ → List.of() copy ก่อนแล้วค่อย shuffle copy
Iterable — collection ที่วนซ้ำได้
Iterable คือ type พื้นฐานของ Dart สำหรับ collection ที่วนซ้ำ (iterate) ได้ — List สืบทอดมาจาก Iterable อีกที
Iterable ← วนซ้ำได้ แต่เข้าถึงด้วย index ไม่ได้
↑
List ← วนซ้ำได้ + เข้าถึงด้วย index ได้ เช่น items[0]
map() และ where() return Iterable ไม่ใช่ List — ถ้าต้องการ List (เช่น ส่งให้ children) ต้องเรียก .toList():
// ❌ Column ไม่รับ Iterable
children: data.map((d) => Text(d)),
// ✅ แปลงเป็น List ก่อน
children: data.map((d) => Text(d)).toList(),Map (Key-Value)
Map คือ collection ของ key/value pairs — key กับ value เป็น type อะไรก็ได้ (key มักเป็น String)
Map<String, Object> user = {
'user_name': 'Maximilian',
'password': 'supersecret',
'age': 33,
};
// อ่าน/แก้ค่าผ่าน []
user['age']; // → 33
user['age'] = 34; // แก้ค่า
// methods ที่ใช้บ่อย
user.containsKey('age'); // → true
user.keys; // → ('user_name', 'password', 'age')
user.values; // → ('Maximilian', 'supersecret', 34)
// List of Maps
List<Map<String, Object>> summary = [];
summary.add({'question': 'What is Flutter?', 'answer': 'A UI toolkit'});Spread Operator (…)
Column(
children: [
const Text('Title'),
...answers.map((a) => AnswerButton(text: a)), // แกะ list ออก
],
)... แกะ list ออกจาก [] ให้กลายเป็น items เดี่ยวๆ — ใช้เมื่อต้องแทรก list ใน list
for…in ใน List — alternative ของ spread + map
final numbers = [5, 6];
// แบบ spread + map
final myList = [
1, 2,
...numbers.map((n) => n * 2), // 5*2=10, 6*2=12
];
// myList → [1, 2, 10, 12]
// แบบ for...in (ผลลัพธ์เหมือนกัน)
final myList2 = [
1, 2,
for (final n in numbers)
n * 2,
];
// myList2 → [1, 2, 10, 12]... + map() | for...in | |
|---|---|---|
| ใช้เมื่อ | แค่แทรก list ตรงๆ หรือ transform สั้นๆ | transform ก่อนเพิ่ม, อ่านง่ายกว่าเมื่อ logic ยาว |
| ไม่ต้อง | — | ใส่ ... เพราะ for แกะให้อัตโนมัติ |
Comparison Operators — ตัวดำเนินการเปรียบเทียบ
ทุกตัวให้ผลลัพธ์เป็น bool เสมอ — เอาไปใช้ใน if, ternary, หรือเก็บในตัวแปรได้
| Operator | ความหมาย | ตัวอย่าง (age = 20) | ผลลัพธ์ |
|---|---|---|---|
== | เท่ากับ | age == 20 | true |
!= | ไม่เท่ากับ | age != 20 | false |
> | มากกว่า | age > 18 | true |
>= | มากกว่าหรือเท่ากับ | age >= 20 | true |
< | น้อยกว่า | age < 25 | true |
<= | น้อยกว่าหรือเท่ากับ | age <= 19 | false |
== vs =: เครื่องหมายเท่ากับ 2 ตัว = เปรียบเทียบ (ได้ bool), ตัวเดียว = กำหนดค่า (assign)
var age = 20; // = กำหนดค่า
if (age == 20) { } // == ถามว่าเท่ากันไหม
bool isAdult = age >= 18; // เก็บผลลัพธ์ bool ในตัวแปรLogical Operators — รวมหลายเงื่อนไข
| Operator | ความหมาย | ผลลัพธ์เป็น true เมื่อ |
|---|---|---|
|| | OR | อย่างน้อย 1 เงื่อนไขเป็น true |
&& | AND | ทุกเงื่อนไขเป็น true |
final amountIsInvalid = enteredAmount == null || enteredAmount <= 0;
if (title.trim().isEmpty || amountIsInvalid || _selectedDate == null) {
// แค่เงื่อนไขเดียวผิด → เข้า block นี้
}String Utilities
' hello '.trim() // → 'hello' (ตัด whitespace หัวท้าย)
''.isEmpty // → true
'hello'.isEmpty // → falseType Parsing
double.tryParse('19.99') // → 19.99 (แปลงได้ → คืน double)
double.tryParse('hello') // → null (แปลงไม่ได้ → คืน null)
int.tryParse('42') // → 42ต่างจาก double.parse() ยังไง? — parse() แปลงไม่ได้จะ throw error / tryParse() คืน null แทน → ปลอดภัยกว่า
if Statement & Conditional Expression
if / else if / else
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else {
grade = 'C';
}Ternary Expression
var result = age >= 18 ? 'ผู้ใหญ่' : 'เด็ก';if ใน List — Collection if
Dart ใช้ if ข้างใน [] ได้เลย — เพิ่ม/ไม่เพิ่ม item ตามเงื่อนไข โดยไม่ต้องสร้าง list แยกแล้วค่อย add
Column(
children: [
const Text('สินค้า'),
if (isLoggedIn) const Text('ยินดีต้อนรับ!'), // แสดงเฉพาะตอน login
if (cartCount > 0) Text('ตะกร้า: $cartCount'),
],
)ทำไมมีประโยชน์ใน Flutter:
- Widget tree ใน Flutter คือ list ของ children → ใช้
ifเลือกแสดง widget ได้ตรงๆ - ไม่ต้องสร้าง list แยก แล้วค่อย
addAll— อ่านง่ายกว่า - ใช้คู่กับ
forใน list ได้:for (var item in items) ListTile(title: Text(item))
Async / Await
// async function คืน Future
Future<void> _submitData() async {
final response = await http.post(url, body: json.encode(data));
// โค้ดหลัง await จะรอจนกว่าจะได้ response
}
// showDatePicker ก็เป็น async
void _presentDatePicker() async {
final pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime.now(),
);
}.then() vs async/await
// วิธี 1: .then() — ซ้อน callback
showDatePicker(...).then((pickedDate) {
setState(() { _selectedDate = pickedDate; });
});
// วิธี 2: async/await — อ่านง่ายกว่า ทำงานเหมือนกัน
void _presentDatePicker() async {
final pickedDate = await showDatePicker(...);
setState(() { _selectedDate = pickedDate; });
}await ทำให้โค้ดรอผลลัพธ์ก่อนไปต่อ — ใช้ได้เฉพาะใน function ที่มี async
การตั้งชื่อ
| สิ่งที่ตั้งชื่อ | รูปแบบ | ตัวอย่าง |
|---|---|---|
| Class | PascalCase | GradientContainer |
| Variable / Function | camelCase | startAlignment |
| File | snake_case | gradient_container.dart |
| Project | snake_case | my_app, first_app |
| Enum | PascalCase (type), camelCase (values) | Category.food |
เปรียบเทียบกับ Java
| ด้าน | Dart | Java |
|---|---|---|
| สร้างโดย | Sun/Oracle | |
| ใช้หลักกับ | Mobile (Flutter) | Backend (Spring Boot) |
| Null safety | มีในตัว (?) | Optional (เวอร์ชันใหม่) |
| Compilation | AOT + JIT | JIT (JVM) |
| สร้าง object | MaterialApp() ไม่ต้อง new | new MaterialApp() |
| Private | _ prefix | private keyword |
| Named constructor | Image.asset() | ไม่มี (ใช้ static factory) |
| String interpolation | 'dice-$roll.png' | "dice-" + roll + ".png" |
| Enum | enum Category { food, travel } | เหมือนกัน |
| Async | async/await | CompletableFuture |
Key Points
- Dart เป็น type-safe language — ทุกค่ามี type กำกับ
const > final > var— ใช้ตัวที่เข้มงวดที่สุดเท่าที่ทำได้- ทุกค่าเป็น Object → ส่ง function เป็น argument ได้
async/awaitทำให้เขียน asynchronous code อ่านง่ายเหมือน synchronous$variableและ${expression}ทำ string interpolation ได้สะดวก- Spread operator
...ใช้แกะ list ใส่ใน list อื่น — ใช้บ่อยใน Flutter widget tree