ในยุคที่ผู้ใช้งานอยู่บนหลายแพลตฟอร์มทั้ง iOS Android เว็บ และ Desktop การพัฒนาแอปพลิเคชันแยกกันสำหรับแต่ละแพลตฟอร์มนั้นใช้ทรัพยากรมหาศาล ทั้งเวลา ค่าใช้จ่าย และทีมนักพัฒนาที่ต้องเชี่ยวชาญแต่ละแพลตฟอร์ม Flutter จาก Google เข้ามาแก้ปัญหานี้ด้วยการให้เราเขียนโค้ดชุดเดียว แล้วรันได้บนทุกแพลตฟอร์มจากฐานโค้ดเดียวกัน
บทความนี้จะพาคุณเรียนรู้ Flutter และภาษา Dart อย่างครบถ้วน ตั้งแต่แนวคิดพื้นฐาน ระบบ Widget การจัดการ State การทำ Navigation ไปจนถึงการ Deploy แอปขึ้น App Store และ Play Store ในปี 2026
Flutter คืออะไร?
Flutter คือ UI Toolkit แบบ Open Source จาก Google ที่ใช้สำหรับสร้างแอปพลิเคชันแบบ Cross-Platform จากฐานโค้ดเดียว สามารถสร้างแอปสำหรับ iOS, Android, Web, Windows, macOS และ Linux ได้ทั้งหมดจากโค้ด Dart ชุดเดียว
Flutter ไม่ได้ใช้ WebView หรือ Native Component ของแต่ละแพลตฟอร์มในการ render UI แต่ใช้ Skia (และตอนนี้ Impeller) เป็น rendering engine ของตัวเอง ทำให้ได้ประสิทธิภาพสูงและหน้าตาเหมือนกันทุกแพลตฟอร์ม โดยสามารถรันได้ที่ 60fps หรือ 120fps บนอุปกรณ์ที่รองรับ
ทำไมต้อง Flutter?
- Codebase เดียว — เขียนครั้งเดียว รันได้ทุกแพลตฟอร์ม ลดเวลาและค่าใช้จ่ายในการพัฒนา
- Hot Reload — เห็นผลลัพธ์ทันทีไม่ต้อง Build ใหม่ ทำให้พัฒนาได้เร็วมาก
- Performance สูง — Compile เป็น Native ARM code ไม่ผ่าน Bridge
- Widget ครบ — มี Widget สำเร็จรูปมากมาย ทั้ง Material Design และ Cupertino (iOS style)
- ชุมชนใหญ่ — Package บน pub.dev มากกว่า 40,000 packages ในปี 2026
- Google สนับสนุน — ใช้ใน Google Ads, Google Pay และแอปภายในของ Google
Dart คืออะไร? ภาษาเบื้องหลัง Flutter
Dart เป็นภาษาโปรแกรมที่พัฒนาโดย Google ออกแบบมาเพื่อสร้าง Client Application โดยเฉพาะ มีไวยากรณ์คล้าย JavaScript, Java และ C# ทำให้นักพัฒนาที่มาจากภาษาเหล่านี้สามารถเรียนรู้ได้อย่างรวดเร็ว Dart รองรับทั้ง AOT (Ahead-of-Time) compilation สำหรับ production และ JIT (Just-in-Time) compilation สำหรับ development ที่ทำให้ Hot Reload เป็นไปได้
ตัวแปรและ Type ใน Dart
// ประกาศตัวแปรแบบระบุ Type
String name = 'Flutter Developer';
int age = 28;
double salary = 85000.50;
bool isActive = true;
List<String> skills = ['Dart', 'Flutter', 'Firebase'];
Map<String, dynamic> user = {'name': 'Bom', 'age': 28};
// Type inference ด้วย var
var city = 'Bangkok'; // Dart รู้ว่าเป็น String
// final vs const
final DateTime now = DateTime.now(); // Runtime constant
const double pi = 3.14159; // Compile-time constant
// late — ประกาศก่อน กำหนดค่าทีหลัง
late String description;
description = 'Initialized later';
Null Safety ใน Dart
Dart มีระบบ Null Safety ที่ช่วยป้องกัน Null Reference Error ซึ่งเป็นบั๊กที่พบบ่อยที่สุดในการพัฒนาซอฟต์แวร์ ทุกตัวแปรจะเป็น Non-nullable โดยค่าเริ่มต้น ถ้าต้องการให้เป็น null ได้ต้องใส่เครื่องหมาย ?
// Non-nullable — ต้องมีค่าเสมอ
String name = 'Flutter';
// Nullable — เป็น null ได้
String? nickname; // ค่าเริ่มต้นเป็น null
// Null-aware operators
String displayName = nickname ?? 'Anonymous'; // ถ้า null ใช้ค่า default
int? length = nickname?.length; // ถ้า null ไม่เรียก .length
nickname ??= 'Default'; // กำหนดค่าถ้ายังเป็น null
// Null assertion (ใช้เมื่อมั่นใจว่าไม่เป็น null)
String definitelyNotNull = nickname!; // จะ throw error ถ้า null จริง
ฟังก์ชันใน Dart
// ฟังก์ชันปกติ
int add(int a, int b) {
return a + b;
}
// Arrow function (สำหรับ expression เดียว)
int multiply(int a, int b) => a * b;
// Named parameters (ใช้บ่อยมากใน Flutter)
void greet({required String name, int age = 25}) {
print('Hello $name, age $age');
}
greet(name: 'Bom', age: 30);
// Optional positional parameters
String introduce(String name, [String? title]) {
return title != null ? '$title $name' : name;
}
// Higher-order function
List<int> numbers = [1, 2, 3, 4, 5];
var doubled = numbers.map((n) => n * 2).toList();
var evens = numbers.where((n) => n % 2 == 0).toList();
Class และ OOP ใน Dart
class User {
final String name;
final int age;
String? email;
// Constructor
User(this.name, this.age, {this.email});
// Named constructor
User.guest() : name = 'Guest', age = 0;
// Factory constructor
factory User.fromJson(Map<String, dynamic> json) {
return User(json['name'], json['age'], email: json['email']);
}
// Method
String greet() => 'Hi, I am $name';
// Override toString
@override
String toString() => 'User(name: $name, age: $age)';
}
// Inheritance
class Admin extends User {
final String role;
Admin(String name, int age, this.role) : super(name, age);
}
// Mixin
mixin Logging {
void log(String message) => print('[LOG] $message');
}
class Service with Logging {
void doWork() {
log('Working...');
}
}
// Abstract class
abstract class Shape {
double area();
double perimeter();
}
class Circle extends Shape {
final double radius;
Circle(this.radius);
@override
double area() => 3.14159 * radius * radius;
@override
double perimeter() => 2 * 3.14159 * radius;
}
Async/Await ใน Dart
// Future — คล้าย Promise ใน JavaScript
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Data loaded';
}
// การใช้งาน
void main() async {
String data = await fetchData();
print(data);
}
// Stream — ข้อมูลที่มาเรื่อยๆ
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// รับ Stream
await for (var count in countStream(5)) {
print('Count: $count');
}
// Future.wait — รอหลาย Future พร้อมกัน
var results = await Future.wait([
fetchUserData(),
fetchSettings(),
fetchNotifications(),
]);
Flutter vs React Native เปรียบเทียบ
| ด้าน | Flutter | React Native |
|---|---|---|
| ภาษา | Dart | JavaScript / TypeScript |
| Rendering | Skia / Impeller (custom) | Native Components + Bridge |
| Performance | สูงมาก (compiled native) | ดี แต่มี bridge overhead |
| Hot Reload | รองรับ (stateful) | รองรับ (Fast Refresh) |
| UI Consistency | เหมือนกันทุก platform | ใช้ native components ต่างกัน |
| Web Support | รองรับ (stable) | ผ่าน React Native Web |
| Desktop | รองรับ (Windows, macOS, Linux) | จำกัด |
| ชุมชน | เติบโตเร็ว | ใหญ่กว่า (อยู่มานาน) |
| Learning Curve | ต้องเรียน Dart ใหม่ | ง่ายกว่าถ้ารู้ JS แล้ว |
| ขนาดแอป | ใหญ่กว่าเล็กน้อย | เล็กกว่าเล็กน้อย |
เลือก React Native เมื่อ: ทีมถนัด JavaScript/TypeScript อยู่แล้ว, ต้องการ native look-and-feel ของแต่ละ platform
ติดตั้ง Flutter
# ดาวน์โหลด Flutter SDK
# Windows: ดาวน์โหลดจาก flutter.dev แล้วเพิ่มใน PATH
# macOS
brew install flutter
# Linux
sudo snap install flutter --classic
# ตรวจสอบการติดตั้ง
flutter doctor
# สร้างโปรเจกต์ใหม่
flutter create my_app
cd my_app
# รันแอป
flutter run # รันบน emulator/device ที่เชื่อมต่อ
flutter run -d chrome # รันบน Chrome (web)
flutter run -d windows # รันบน Windows (desktop)
# Hot Reload: กด 'r' ใน terminal
# Hot Restart: กด 'R' ใน terminal
Widget — หัวใจของ Flutter
ใน Flutter ทุกอย่างคือ Widget ตั้งแต่ปุ่มกด ข้อความ จนถึง Layout ทั้งหมดเป็น Widget ที่ซ้อนกันเป็นโครงสร้างแบบ Tree เรียกว่า Widget Tree
StatelessWidget — Widget ที่ไม่มี State
import 'package:flutter/material.dart';
class WelcomeCard extends StatelessWidget {
final String name;
final String role;
const WelcomeCard({
super.key,
required this.name,
required this.role,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(role, style: const TextStyle(color: Colors.grey)),
],
),
),
);
}
}
StatefulWidget — Widget ที่มี 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(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $_count', style: const TextStyle(fontSize: 48)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}
Widget Lifecycle
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
@override
void initState() {
super.initState();
// เรียกครั้งเดียวตอนสร้าง Widget
// โหลดข้อมูลเริ่มต้น, subscribe stream
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// เรียกเมื่อ dependency เปลี่ยน (เช่น InheritedWidget)
}
@override
void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// เรียกเมื่อ parent rebuild ด้วย config ใหม่
}
@override
void dispose() {
// เรียกตอน Widget ถูกลบ
// ทำ cleanup: cancel stream, dispose controller
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Layout — การจัดวาง Widget
Row, Column, Stack
// Row — เรียงแนวนอน
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.star),
Text('Rating'),
Text('4.5'),
],
)
// Column — เรียงแนวตั้ง
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Title'),
SizedBox(height: 8),
Text('Description'),
Spacer(), // ดัน Widget ไปล่าง
ElevatedButton(onPressed: () {}, child: Text('Action')),
],
)
// Stack — ซ้อนทับกัน
Stack(
alignment: Alignment.center,
children: [
Image.network('https://example.com/bg.jpg'),
Positioned(
bottom: 16,
left: 16,
child: Text('Overlay Text',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
],
)
Scaffold — โครงสร้างหน้าจอมาตรฐาน
Scaffold(
appBar: AppBar(
title: const Text('My App'),
actions: [
IconButton(icon: Icon(Icons.search), onPressed: () {}),
],
),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(child: Text('Menu')),
ListTile(title: Text('Home'), onTap: () {}),
ListTile(title: Text('Settings'), onTap: () {}),
],
),
),
body: Center(child: Text('Content')),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
)
Container และ Decoration
Container(
width: 200,
height: 100,
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
border: Border.all(color: Colors.blue, width: 1),
),
child: const Text('Styled Container'),
)
ListView และ GridView
// ListView.builder — สำหรับ list ยาวๆ (สร้าง item เฉพาะที่มองเห็น)
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text(items[index].title),
subtitle: Text(items[index].description),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () => navigateToDetail(items[index]),
);
},
)
// GridView.builder — ตารางแบบ dynamic
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 0.75,
),
itemCount: products.length,
itemBuilder: (context, index) {
return ProductCard(product: products[index]);
},
)
Navigation — การเปลี่ยนหน้า
Navigator พื้นฐาน
// Push — ไปหน้าใหม่
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailPage(item: item)),
);
// Pop — กลับหน้าเดิม
Navigator.pop(context);
// Push and replace — แทนที่หน้าปัจจุบัน
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
// Push and remove all — ล้างทุกหน้าแล้วไปหน้าใหม่
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
(route) => false,
);
go_router — Navigation ที่ทันสมัย
// pubspec.yaml: go_router: ^14.0.0
final GoRouter router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'detail/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailPage(id: id);
},
),
],
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsPage(),
),
],
redirect: (context, state) {
final isLoggedIn = authService.isLoggedIn;
if (!isLoggedIn && state.matchedLocation != '/login') {
return '/login';
}
return null;
},
);
// ใช้งาน
MaterialApp.router(routerConfig: router);
// Navigate
context.go('/detail/123');
context.push('/settings');
context.pop();
State Management — จัดการ State
State Management เป็นหัวข้อสำคัญที่สุดอย่างหนึ่งของ Flutter เพราะ Flutter เป็น Declarative UI Framework หมายความว่า UI จะถูกสร้างใหม่ทุกครั้งที่ State เปลี่ยน การเลือกวิธีจัดการ State ที่เหมาะสมจึงสำคัญมาก
setState — วิธีพื้นฐาน
// เหมาะกับ State ที่ใช้ภายใน Widget เดียว
class _MyWidgetState extends State<MyWidget> {
int count = 0;
bool isLoading = false;
List<String> items = [];
Future<void> loadData() async {
setState(() => isLoading = true);
items = await api.fetchItems();
setState(() => isLoading = false);
}
@override
Widget build(BuildContext context) {
if (isLoading) return CircularProgressIndicator();
return ListView.builder(
itemCount: items.length,
itemBuilder: (ctx, i) => Text(items[i]),
);
}
}
Provider — แนะนำโดย Flutter Team
// 1. สร้าง Model
class CartModel extends ChangeNotifier {
final List<Product> _items = [];
List<Product> get items => List.unmodifiable(_items);
int get totalItems => _items.length;
double get totalPrice => _items.fold(0, (sum, item) => sum + item.price);
void add(Product product) {
_items.add(product);
notifyListeners(); // บอก Widget ให้ rebuild
}
void remove(Product product) {
_items.remove(product);
notifyListeners();
}
}
// 2. Provide ที่ระดับบนสุด
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CartModel(),
child: const MyApp(),
),
);
}
// 3. ใช้ใน Widget
class CartIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cart = context.watch<CartModel>();
return Badge(
label: Text('${cart.totalItems}'),
child: Icon(Icons.shopping_cart),
);
}
}
class AddToCartButton extends StatelessWidget {
final Product product;
const AddToCartButton({required this.product});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => context.read<CartModel>().add(product),
child: const Text('Add to Cart'),
);
}
}
Riverpod — Provider ยุคใหม่
// Riverpod เป็น State Management ที่ปลอดภัยกว่า Provider
// ไม่พึ่ง BuildContext, compile-safe, testable
// 1. สร้าง Provider
final counterProvider = StateProvider<int>((ref) => 0);
final todosProvider = StateNotifierProvider<TodoNotifier, List<Todo>>((ref) {
return TodoNotifier();
});
class TodoNotifier extends StateNotifier<List<Todo>> {
TodoNotifier() : super([]);
void add(Todo todo) {
state = [...state, todo];
}
void toggle(String id) {
state = state.map((t) => t.id == id ? t.copyWith(done: !t.done) : t).toList();
}
}
// Async provider — โหลดข้อมูลจาก API
final userProvider = FutureProvider<User>((ref) async {
final response = await http.get(Uri.parse('/api/user'));
return User.fromJson(jsonDecode(response.body));
});
// 2. ใช้ใน Widget
class TodoList extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final todos = ref.watch(todosProvider);
return ListView(
children: todos.map((t) => TodoItem(todo: t)).toList(),
);
}
}
BLoC Pattern — Business Logic Component
// BLoC แยก Business Logic ออกจาก UI อย่างชัดเจน
// Events
abstract class AuthEvent {}
class LoginEvent extends AuthEvent {
final String email;
final String password;
LoginEvent(this.email, this.password);
}
class LogoutEvent extends AuthEvent {}
// States
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthSuccess extends AuthState {
final User user;
AuthSuccess(this.user);
}
class AuthError extends AuthState {
final String message;
AuthError(this.message);
}
// BLoC
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository repository;
AuthBloc(this.repository) : super(AuthInitial()) {
on<LoginEvent>((event, emit) async {
emit(AuthLoading());
try {
final user = await repository.login(event.email, event.password);
emit(AuthSuccess(user));
} catch (e) {
emit(AuthError(e.toString()));
}
});
on<LogoutEvent>((event, emit) async {
await repository.logout();
emit(AuthInitial());
});
}
}
// ใช้ใน Widget
BlocBuilder<AuthBloc, AuthState>(
builder: (context, state) {
if (state is AuthLoading) return CircularProgressIndicator();
if (state is AuthSuccess) return Text('Welcome ${state.user.name}');
if (state is AuthError) return Text('Error: ${state.message}');
return LoginForm();
},
)
Material Design 3 ใน Flutter
MaterialApp(
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.blue,
brightness: Brightness.light,
),
darkTheme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.blue,
brightness: Brightness.dark,
),
themeMode: ThemeMode.system, // ตาม system setting
home: const HomePage(),
)
// Material 3 Widgets
FilledButton(onPressed: () {}, child: Text('Primary')),
FilledButton.tonal(onPressed: () {}, child: Text('Secondary')),
OutlinedButton(onPressed: () {}, child: Text('Outlined')),
// Search Bar
SearchAnchor(
builder: (context, controller) {
return SearchBar(
controller: controller,
hintText: 'Search...',
onTap: () => controller.openView(),
);
},
suggestionsBuilder: (context, controller) {
return suggestions.map((s) => ListTile(
title: Text(s),
onTap: () => controller.closeView(s),
));
},
)
// Navigation Bar (แทน BottomNavigationBar)
NavigationBar(
selectedIndex: currentIndex,
onDestinationSelected: (index) => setState(() => currentIndex = index),
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
NavigationDestination(icon: Icon(Icons.explore), label: 'Explore'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
],
)
Networking — เชื่อมต่อ API
http package พื้นฐาน
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<List<Post>> fetchPosts() async {
final response = await http.get(
Uri.parse('https://api.example.com/posts'),
headers: {'Authorization': 'Bearer $token'},
);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
return data.map((json) => Post.fromJson(json)).toList();
} else {
throw Exception('Failed to load posts: ${response.statusCode}');
}
}
// POST request
Future<Post> createPost(String title, String body) async {
final response = await http.post(
Uri.parse('https://api.example.com/posts'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({'title': title, 'body': body}),
);
if (response.statusCode == 201) {
return Post.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to create post');
}
}
Dio — HTTP Client ที่ทรงพลัง
import 'package:dio/dio.dart';
final dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {'Content-Type': 'application/json'},
));
// Interceptor สำหรับ Auth Token
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
options.headers['Authorization'] = 'Bearer $token';
handler.next(options);
},
onError: (error, handler) {
if (error.response?.statusCode == 401) {
// Token expired — refresh token
refreshToken().then((_) => handler.resolve(
await dio.fetch(error.requestOptions),
));
} else {
handler.next(error);
}
},
));
// GET with query parameters
final response = await dio.get('/posts', queryParameters: {
'page': 1,
'limit': 20,
'category': 'tech',
});
// Upload file
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile('/path/to/image.jpg'),
'name': 'profile_photo',
});
await dio.post('/upload', data: formData);
Local Storage — เก็บข้อมูลในเครื่อง
shared_preferences — key-value storage
import 'package:shared_preferences/shared_preferences.dart';
// บันทึก
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', 'bom');
await prefs.setInt('loginCount', 5);
await prefs.setBool('darkMode', true);
// อ่าน
String? username = prefs.getString('username');
int loginCount = prefs.getInt('loginCount') ?? 0;
bool darkMode = prefs.getBool('darkMode') ?? false;
// ลบ
await prefs.remove('username');
await prefs.clear(); // ลบทั้งหมด
Hive — NoSQL Database ที่เร็วมาก
import 'package:hive_flutter/hive_flutter.dart';
// เริ่มต้น
await Hive.initFlutter();
Hive.registerAdapter(TaskAdapter()); // สำหรับ custom object
var box = await Hive.openBox<Task>('tasks');
// CRUD operations
await box.add(Task(title: 'Learn Flutter', done: false));
Task? task = box.getAt(0);
await box.putAt(0, task!.copyWith(done: true));
await box.deleteAt(0);
// Query
var pendingTasks = box.values.where((t) => !t.done).toList();
sqflite — SQLite Database
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static Database? _database;
Future<Database> get database async {
_database ??= await _initDB();
return _database!;
}
Future<Database> _initDB() async {
String path = join(await getDatabasesPath(), 'app.db');
return openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute(
'CREATE TABLE tasks ('
'id INTEGER PRIMARY KEY AUTOINCREMENT, '
'title TEXT NOT NULL, '
'done INTEGER DEFAULT 0, '
'created_at TEXT DEFAULT CURRENT_TIMESTAMP)'
);
},
);
}
Future<int> insertTask(Map<String, dynamic> task) async {
final db = await database;
return db.insert('tasks', task);
}
Future<List<Map<String, dynamic>>> getTasks() async {
final db = await database;
return db.query('tasks', orderBy: 'created_at DESC');
}
}
Firebase Integration
// pubspec.yaml
// firebase_core, firebase_auth, cloud_firestore, firebase_storage
// เริ่มต้น Firebase
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
// Authentication
final auth = FirebaseAuth.instance;
// Sign up
UserCredential credential = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
// Sign in
await auth.signInWithEmailAndPassword(email: email, password: password);
// Google Sign In
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication googleAuth = await googleUser!.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await auth.signInWithCredential(credential);
// Firestore CRUD
final firestore = FirebaseFirestore.instance;
// Create
await firestore.collection('users').doc(uid).set({
'name': 'Bom',
'email': 'bom@example.com',
'createdAt': FieldValue.serverTimestamp(),
});
// Read (real-time)
firestore.collection('posts').snapshots().listen((snapshot) {
for (var doc in snapshot.docs) {
print('${doc.id}: ${doc.data()}');
}
});
// Query
final techPosts = await firestore
.collection('posts')
.where('category', isEqualTo: 'tech')
.orderBy('createdAt', descending: true)
.limit(10)
.get();
Platform-Specific Code ด้วย MethodChannel
// Dart side (Flutter)
const platform = MethodChannel('com.example.app/battery');
Future<int> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return result;
} on PlatformException catch (e) {
print('Failed: ${e.message}');
return -1;
}
}
// Kotlin side (Android) — MainActivity.kt
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
"com.example.app/battery").setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
result.success(batteryLevel)
} else {
result.notImplemented()
}
}
}
}
// Swift side (iOS) — AppDelegate.swift
// override func application(...) {
// let controller = window?.rootViewController as! FlutterViewController
// let channel = FlutterMethodChannel(name: "com.example.app/battery",
// binaryMessenger: controller.binaryMessenger)
// channel.setMethodCallHandler { (call, result) in
// if call.method == "getBatteryLevel" {
// result(UIDevice.current.batteryLevel * 100)
// }
// }
// }
Flutter for Web และ Desktop
Flutter Web
# สร้างโปรเจกต์ที่รองรับ Web
flutter create --platforms=web,android,ios my_app
# รันบน Web
flutter run -d chrome
# Build สำหรับ production
flutter build web --release
# Output อยู่ที่ build/web/
# สามารถ deploy ไป hosting ใดก็ได้
Flutter Web ใช้ได้ดีกับแอปที่เน้น interaction เช่น dashboard, admin panel, portfolio แต่ไม่เหมาะกับเว็บที่ต้องการ SEO เพราะ content ถูก render ด้วย Canvas ไม่ใช่ HTML ปกติ ในปี 2026 Flutter Web ได้ปรับปรุงเรื่อง Accessibility และ SEO ให้ดีขึ้นมากด้วย HTML renderer
Flutter Desktop
# เปิดใช้ Desktop support
flutter config --enable-windows-desktop
flutter config --enable-macos-desktop
flutter config --enable-linux-desktop
# รัน
flutter run -d windows
flutter run -d macos
flutter run -d linux
# Build
flutter build windows --release
flutter build macos --release
Flutter Desktop เหมาะสำหรับสร้างเครื่องมือภายในองค์กร, แอปจัดการข้อมูล, หรือแอปที่ต้องการ cross-platform desktop ผลลัพธ์ได้แอปที่มี native performance เพราะ compile เป็น native code จริงๆ ไม่ผ่าน Electron หรือ WebView
Testing ใน Flutter
Unit Test
// test/cart_test.dart
import 'package:test/test.dart';
void main() {
group('CartModel', () {
late CartModel cart;
setUp(() {
cart = CartModel();
});
test('starts empty', () {
expect(cart.totalItems, 0);
expect(cart.totalPrice, 0.0);
});
test('add item increases count', () {
cart.add(Product(name: 'Widget', price: 100));
expect(cart.totalItems, 1);
expect(cart.totalPrice, 100.0);
});
test('remove item decreases count', () {
final product = Product(name: 'Widget', price: 100);
cart.add(product);
cart.remove(product);
expect(cart.totalItems, 0);
});
});
}
Widget Test
// test/counter_widget_test.dart
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: CounterWidget()));
// ตรวจว่าเริ่มที่ 0
expect(find.text('Count: 0'), findsOneWidget);
// กดปุ่ม
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
// ตรวจว่าเพิ่มเป็น 1
expect(find.text('Count: 1'), findsOneWidget);
});
testWidgets('shows loading indicator', (tester) async {
await tester.pumpWidget(const MaterialApp(home: MyPage()));
expect(find.byType(CircularProgressIndicator), findsOneWidget);
});
}
Integration Test
// integration_test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('full app flow', (tester) async {
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
// Login
await tester.enterText(find.byKey(Key('email')), 'test@test.com');
await tester.enterText(find.byKey(Key('password')), 'password');
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
// ตรวจว่าเข้า Home page
expect(find.text('Welcome'), findsOneWidget);
});
}
// รันด้วย:
// flutter test integration_test/app_test.dart
Performance Optimization
เทคนิคเพิ่ม Performance
// 1. ใช้ const constructor เมื่อ Widget ไม่เปลี่ยนแปลง
const Text('Static text') // ไม่ต้อง rebuild
const SizedBox(height: 16)
const EdgeInsets.all(16)
// 2. ใช้ Key อย่างถูกต้อง
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return MyItem(
key: ValueKey(items[index].id), // ช่วย Flutter track item
item: items[index],
);
},
)
// 3. แยก Widget ย่อยเพื่อลด rebuild scope
// แทนที่จะให้ทั้งหน้า rebuild ให้เฉพาะส่วนที่เปลี่ยน rebuild
class PriceDisplay extends StatelessWidget {
final double price;
const PriceDisplay({required this.price, super.key});
@override
Widget build(BuildContext context) {
return Text('฿${price.toStringAsFixed(2)}');
}
}
// 4. Lazy Loading ด้วย ListView.builder แทน ListView
// ListView.builder สร้าง widget เฉพาะที่มองเห็นบนหน้าจอ
// 5. Cache รูปภาพ
CachedNetworkImage(
imageUrl: 'https://example.com/photo.jpg',
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
// 6. ใช้ RepaintBoundary สำหรับส่วนที่ animate
RepaintBoundary(
child: AnimatedWidget(),
)
Deployment — ส่งแอปขึ้น Store
Android — Play Store
# 1. สร้าง Keystore
keytool -genkey -v -keystore ~/upload-keystore.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias upload
# 2. สร้าง key.properties
# storePassword=xxxxx
# keyPassword=xxxxx
# keyAlias=upload
# storeFile=/path/to/upload-keystore.jks
# 3. Build APK หรือ App Bundle
flutter build appbundle --release
# Output: build/app/outputs/bundle/release/app-release.aab
# อัปโหลดไป Google Play Console
iOS — App Store
# 1. เปิด Xcode
open ios/Runner.xcworkspace
# 2. ตั้ง Bundle Identifier, Signing Team
# 3. Build
flutter build ipa --release
# Output: build/ios/ipa/
# อัปโหลดผ่าน Transporter หรือ Xcode
Dart Packages ที่ต้องรู้ (pub.dev)
| Package | หน้าที่ | การใช้งาน |
|---|---|---|
provider | State Management | จัดการ State แบบง่าย |
riverpod | State Management | State Management แบบ compile-safe |
flutter_bloc | State Management | BLoC pattern |
dio | HTTP Client | Network requests ขั้นสูง |
go_router | Navigation | Declarative routing |
hive | Database | NoSQL local storage |
sqflite | Database | SQLite local database |
freezed | Code Generation | Immutable data classes |
json_serializable | Serialization | JSON parsing อัตโนมัติ |
cached_network_image | Image | Cache รูปจาก URL |
flutter_svg | Image | แสดง SVG |
intl | Localization | แปลภาษา, format วันที่ |
flutter_secure_storage | Security | เก็บข้อมูล sensitive |
url_launcher | Utility | เปิด URL, email, phone |
Flutter DevTools
Flutter DevTools เป็นชุดเครื่องมือสำหรับ debug และ profile แอป Flutter มีเครื่องมือหลายตัว ได้แก่ Widget Inspector สำหรับดูโครงสร้าง Widget Tree, Performance Profiler สำหรับวิเคราะห์ Frame Rate และ Jank, Memory Profiler สำหรับตรวจจับ Memory Leak, Network Profiler สำหรับดู HTTP requests ทั้งหมด และ Logging Console สำหรับดู log messages
# เปิด DevTools
flutter pub global activate devtools
flutter pub global run devtools
# หรือเปิดจาก IDE
# VS Code: Ctrl+Shift+P -> Flutter: Open DevTools
# Android Studio: View -> Tool Windows -> Flutter Inspector
# Profile mode (วัด performance จริง)
flutter run --profile
# Release mode (ทดสอบก่อน deploy)
flutter run --release
โครงสร้างโปรเจกต์ Flutter ที่แนะนำ
lib/
main.dart # Entry point
app.dart # MaterialApp configuration
core/
constants/ # ค่าคงที่, สี, ขนาด
theme/ # ThemeData
utils/ # Utility functions
network/ # Dio setup, interceptors
features/
auth/
data/
models/ # Data models
repositories/ # Data layer
datasources/ # API calls, local DB
domain/
entities/ # Business entities
usecases/ # Business logic
presentation/
pages/ # Full screen widgets
widgets/ # Reusable widgets
bloc/ # BLoC / Provider
home/
...
settings/
...
shared/
widgets/ # Widget ที่ใช้ร่วมกัน
models/ # Model ที่ใช้ร่วมกัน
สรุป
Flutter เป็นเครื่องมือที่ทรงพลังสำหรับการพัฒนา Cross-Platform Application ในปี 2026 ด้วย Dart ที่เป็นภาษาที่เรียนรู้ง่าย มี Null Safety และ Async/Await ที่ยอดเยี่ยม ประกอบกับระบบ Widget ที่ยืดหยุ่น การจัดการ State ที่มีหลายทางเลือก และ Performance ที่ใกล้เคียง Native ทำให้ Flutter เป็นตัวเลือกอันดับหนึ่งสำหรับทีมที่ต้องการสร้างแอปที่รันบนหลายแพลตฟอร์ม
เริ่มต้นด้วยการเรียนรู้ Dart ให้แม่น แล้วเข้าใจระบบ Widget จากนั้นเลือก State Management ที่เหมาะกับขนาดโปรเจกต์ ฝึกทำ Navigation และ Networking แล้วเพิ่มเรื่อง Testing และ Performance Optimization เมื่อแอปเติบโตขึ้น Flutter มีอนาคตที่สดใสและ Google ยังคงลงทุนพัฒนาอย่างต่อเนื่อง เป็นทักษะที่คุ้มค่าที่จะลงทุนเรียนรู้
