บทที่ 9: Flutter และ Dart พื้นฐาน
สารบัญเนื้อหา
บทที่ 9: Flutter และ Dart พื้นฐาน
🎯 เป้าหมายการเรียนรู้
หลังจากเรียนบทนี้แล้ว คุณจะสามารถ:
- เข้าใจว่า Flutter คืออะไร และทำไมถึงเป็นที่นิยม
- เข้าใจพื้นฐาน Dart Language
- สร้าง Widget และเข้าใจ Widget Tree
- แยกความแตกต่างระหว่าง StatelessWidget และ StatefulWidget
- สร้าง UI ด้วย Material Design
📚 ทฤษฎีและแนวคิด
Flutter คืออะไร?
Flutter คือ UI Framework สำหรับสร้าง Cross-platform Applications โดย Google
ข้อดีของ Flutter:
- 📱 Cross-platform: เขียนโค้ดครั้งเดียว รันได้ทั้ง iOS, Android, Web, Desktop
- 🎨 Beautiful UI: Material Design และ Cupertino (iOS-style) widgets
- ⚡ Hot Reload: เห็นผลการแก้ไขโค้ดทันที (ไม่ต้อง rebuild)
- 🚀 Performance: Compile เป็น Native Code (เร็วเท่า Native Apps)
- 💙 Dart Language: ภาษาที่เรียนรู้ง่าย คล้าย JavaScript
Dart Language พื้นฐาน
1. Variables (ตัวแปร)
// Type inference (Dart รู้ type เอง)
var name = "John";
var age = 25;
var isActive = true;
// Explicit type
String email = "john@example.com";
int count = 10;
double price = 99.99;
bool isDark = false;
// Final (กำหนดค่าได้ครั้งเดียว)
final userId = "123";
// Const (ค่าคงที่ตอน compile)
const apiUrl = "https://api.example.com";
2. Functions (ฟังก์ชัน)
// Function with return type
String greet(String name) {
return "Hello, $name!";
}
// Arrow function (สำหรับ expression เดียว)
String greet(String name) => "Hello, $name!";
// Optional parameters
void printInfo(String name, {int? age, String? city}) {
print("Name: $name");
if (age != null) print("Age: $age");
if (city != null) print("City: $city");
}
// Usage
printInfo("John", age: 25);
printInfo("Jane", city: "Bangkok");
3. Null Safety
// Non-nullable (ห้ามเป็น null)
String name = "John";
// name = null; // ❌ Error!
// Nullable (อนุญาตให้เป็น null)
String? email; // email เป็น null ได้
email = "test@example.com";
email = null; // ✅ OK
// Null-aware operators
String? username;
print(username ?? "Guest"); // ถ้า username เป็น null ใช้ "Guest"
String? displayName;
print(displayName?.toUpperCase()); // ถ้า displayName เป็น null จะไม่เรียก toUpperCase()
4. Collections
// List (Array)
List<String> fruits = ["Apple", "Banana", "Orange"];
fruits.add("Mango");
print(fruits[0]); // Apple
print(fruits.length); // 4
// Map (Dictionary/Object)
Map<String, dynamic> user = {
"name": "John",
"age": 25,
"email": "john@example.com"
};
print(user["name"]); // John
// Set (ไม่มีค่าซ้ำ)
Set<int> numbers = {1, 2, 3, 3, 4};
print(numbers); // {1, 2, 3, 4}
5. Classes
class User {
String name;
int age;
// Constructor
User(this.name, this.age);
// Named constructor
User.guest() : name = "Guest", age = 0;
// Method
void introduce() {
print("Hi, I'm $name, $age years old");
}
}
// Usage
var user1 = User("John", 25);
user1.introduce(); // Hi, I'm John, 25 years old
var guest = User.guest();
guest.introduce(); // Hi, I'm Guest, 0 years old
💻 โค้ดจริงจาก QUOM App
1. Main Entry Point
ไฟล์: mobile/lib/main.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'screens/splash_screen.dart';
import 'services/theme_controller.dart';
import 'services/language_controller.dart';
import 'theme/app_theme.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const QuomApp());
}
อธิบายทีละบรรทัด:
-
Import Statements
import 'package:flutter/material.dart';- นำเข้า Material Design widgets
material.dartมี widgets พื้นฐานทั้งหมด (Text, Button, Container, etc.)
-
main() Function
void main() { WidgetsFlutterBinding.ensureInitialized(); runApp(const QuomApp()); }main(): จุดเริ่มต้นของ Dart programWidgetsFlutterBinding.ensureInitialized(): เตรียม Flutter frameworkrunApp(): รัน Flutter application
2. QuomApp Widget (Root Widget)
class QuomApp extends StatelessWidget {
const QuomApp({super.key});
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: ThemeController.instance.themeMode,
builder: (context, mode, child) {
return ValueListenableBuilder<Locale>(
valueListenable: LanguageController.instance.locale,
builder: (context, locale, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'QUOM',
theme: _enhanceWithFonts(AppTheme.light()),
darkTheme: _enhanceWithFonts(AppTheme.dark()),
themeMode: mode,
locale: locale,
home: const SplashScreen(),
);
},
);
},
);
}
}
อธิบายแนวคิด:
StatelessWidget
class QuomApp extends StatelessWidget {
const QuomApp({super.key});
คำอธิบาย:
- StatelessWidget: Widget ที่ไม่มี state (ข้อมูลไม่เปลี่ยน)
const: Constructor เป็น const (ประหยัด memory)super.key: ส่ง key ไปยัง parent class
build() Method
@override
Widget build(BuildContext context) {
return MaterialApp(...);
}
คำอธิบาย:
build(): Method ที่ return Widget tree@override: บอกว่า override method จาก parent classBuildContext: ข้อมูลเกี่ยวกับตำแหน่งของ widget ใน tree
MaterialApp
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'QUOM',
theme: _enhanceWithFonts(AppTheme.light()),
darkTheme: _enhanceWithFonts(AppTheme.dark()),
themeMode: mode,
locale: locale,
home: const SplashScreen(),
);
คำอธิบาย:
MaterialApp: Root widget สำหรับ Material Design appdebugShowCheckedModeBanner: false: ซ่อน "DEBUG" bannertitle: ชื่อ app (แสดงใน task switcher)theme: Theme สำหรับ light modedarkTheme: Theme สำหรับ dark modethemeMode: โหมดปัจจุบัน (light/dark/system)locale: ภาษาที่ใช้home: หน้าแรกของ app
3. ValueListenableBuilder (Reactive UI)
return ValueListenableBuilder<ThemeMode>(
valueListenable: ThemeController.instance.themeMode,
builder: (context, mode, child) {
// UI จะ rebuild เมื่อ themeMode เปลี่ยน
return MaterialApp(
themeMode: mode,
...
);
},
);
คำอธิบาย:
ValueListenableBuilder: Widget ที่ rebuild เมื่อค่าเปลี่ยนvalueListenable: ค่าที่ต้องการ listenbuilder: Function ที่สร้าง UI- Reactive: เมื่อ
themeModeเปลี่ยน UI จะ update อัตโนมัติ
4. Custom Theme Enhancement
ThemeData _enhanceWithFonts(ThemeData theme) {
final textTheme = GoogleFonts.promptTextTheme(theme.textTheme);
return theme.copyWith(
textTheme: textTheme.copyWith(
displayLarge: GoogleFonts.prompt(
color: theme.colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
titleLarge: GoogleFonts.prompt(
color: theme.colorScheme.onSurface,
fontWeight: FontWeight.w700,
),
bodyLarge: GoogleFonts.prompt(
color: theme.colorScheme.onSurface,
height: 1.5,
),
),
);
}
คำอธิบาย:
GoogleFonts.promptTextTheme(): ใช้ฟอนต์ Prompt (รองรับภาษาไทย)theme.copyWith(): สร้าง theme ใหม่โดยแก้ไขบางส่วนFontWeight.bold: น้ำหนักฟอนต์ (bold = 700)height: 1.5: ระยะห่างระหว่างบรรทัด (line height)
🎨 StatelessWidget vs StatefulWidget
StatelessWidget (ไม่มี State)
class GreetingWidget extends StatelessWidget {
final String name;
const GreetingWidget({required this.name, super.key});
@override
Widget build(BuildContext context) {
return Text("Hello, $name!");
}
}
// Usage
GreetingWidget(name: "John")
เมื่อไหร่ใช้:
- UI ที่ไม่เปลี่ยนแปลง
- รับ data จาก parent เท่านั้น
StatefulWidget (มี State)
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int count = 0;
void increment() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text("Count: $count"),
ElevatedButton(
onPressed: increment,
child: Text("Increment"),
),
],
);
}
}
คำอธิบาย:
StatefulWidget: Widget ที่มี state (ข้อมูลเปลี่ยนได้)State<CounterWidget>: Class ที่เก็บ statesetState(): บอก Flutter ให้ rebuild widget- เมื่อไหร่ใช้: UI ที่ต้องเปลี่ยนแปลงตาม user interaction
🏗️ Widget Tree Concept
MaterialApp
└─ SplashScreen
└─ Scaffold
├─ AppBar
│ └─ Text
└─ Body
└─ Center
└─ Column
├─ Image
├─ Text
└─ CircularProgressIndicator
แนวคิด:
- Flutter UI เป็น tree ของ widgets
- Widget แต่ละตัวสามารถมี children ได้
- Parent widget ควบคุม layout ของ children
🎨 Common Widgets
1. Text
Text(
"Hello, World!",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
2. Container
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Text("Container"),
)
3. Column & Row
// Column (แนวตั้ง)
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Item 1"),
Text("Item 2"),
Text("Item 3"),
],
)
// Row (แนวนอน)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.home),
Icon(Icons.search),
Icon(Icons.settings),
],
)
4. ElevatedButton
ElevatedButton(
onPressed: () {
print("Button pressed!");
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text("Click Me"),
)
5. TextField
TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: "Email",
hintText: "Enter your email",
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
)
📝 แบบฝึกหัด
คำถามทบทวน
- Flutter คืออะไร และมีข้อดีอย่างไร?
- StatelessWidget และ StatefulWidget ต่างกันอย่างไร?
setState()ทำหน้าที่อะไร?- Widget Tree คืออะไร?
- MaterialApp ใช้ทำอะไร?
โจทย์ปฏิบัติ
โจทย์ที่ 1: สร้าง StatelessWidget
// TODO: สร้าง ProfileCard widget ที่รับ name และ email
// แสดงเป็น Card พร้อม Icon, ชื่อ, และ email
โจทย์ที่ 2: สร้าง StatefulWidget
// TODO: สร้าง TodoItem widget ที่มี checkbox
// เมื่อกด checkbox ให้ขีดฆ่าข้อความ
โจทย์ที่ 3: สร้าง Layout
// TODO: สร้างหน้า Login ที่มี:
// - Logo (Image)
// - Email TextField
// - Password TextField
// - Login Button
// ใช้ Column, Container, และ TextField
📌 สรุป
ในบทนี้เราได้เรียนรู้:
✅ Flutter คือ Cross-platform UI Framework
✅ Dart คือภาษาที่ใช้เขียน Flutter
✅ StatelessWidget สำหรับ UI ที่ไม่เปลี่ยน
✅ StatefulWidget สำหรับ UI ที่มี state
✅ Widget Tree คือโครงสร้างของ Flutter UI
✅ Material Design widgets สำหรับสร้าง UI สวยงาม
ไฟล์ที่เกี่ยวข้อง
mobile/lib/main.dart- Main entry pointmobile/pubspec.yaml- Dependencies
🔜 บทถัดไป
บทที่ 10: App Architecture และ State Management
เราจะเรียนรู้การจัดโครงสร้างโปรเจค, Service Layer, และการจัดการ State