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 และ Object

Core Types

Typeคืออะไรตัวอย่าง
intจำนวนเต็ม29, -15
doubleทศนิยม3.91, -12.81
numเต็มหรือทศนิยม15, 15.01
Stringข้อความ'Hello World'
boolจริง/เท็จtrue, false
Objecttype พื้นฐานของทุกค่าอะไรก็ได้

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

varfinalconst
เปลี่ยนค่าได้ไหมได้ไม่ได้ไม่ได้
รู้ค่าตอนไหนตอนรันตอนรันตอน compile
เทียบ Javaตัวแปรธรรมดาfinalstatic 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 error
const 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-collect

List.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);  // สลับลำดับได้
PositionalNamed
ลำดับสำคัญไม่สำคัญ (ใช้ชื่อ)
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}';                    // expression

Java ต้องต่อ 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 == 20true
!=ไม่เท่ากับage != 20false
>มากกว่าage > 18true
>=มากกว่าหรือเท่ากับage >= 20true
<น้อยกว่าage < 25true
<=น้อยกว่าหรือเท่ากับage <= 19false

== 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           // → false

Type 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

การตั้งชื่อ

สิ่งที่ตั้งชื่อรูปแบบตัวอย่าง
ClassPascalCaseGradientContainer
Variable / FunctioncamelCasestartAlignment
Filesnake_casegradient_container.dart
Projectsnake_casemy_app, first_app
EnumPascalCase (type), camelCase (values)Category.food

เปรียบเทียบกับ Java

ด้านDartJava
สร้างโดยGoogleSun/Oracle
ใช้หลักกับMobile (Flutter)Backend (Spring Boot)
Null safetyมีในตัว (?)Optional (เวอร์ชันใหม่)
CompilationAOT + JITJIT (JVM)
สร้าง objectMaterialApp() ไม่ต้อง newnew MaterialApp()
Private_ prefixprivate keyword
Named constructorImage.asset()ไม่มี (ใช้ static factory)
String interpolation'dice-$roll.png'"dice-" + roll + ".png"
Enumenum Category { food, travel }เหมือนกัน
Asyncasync/awaitCompletableFuture

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
  • Flutter — UI framework ที่ใช้ Dart
  • Widgets — building blocks ของ Flutter เขียนด้วย Dart
  • OOP — Dart เป็นภาษา Object-Oriented เต็มรูปแบบ
  • Java — ภาษาที่มีแนวคิดคล้ายกัน แต่ใช้คนละ platform