Home > Blog > tech

Flutter และ Dart คืออะไร? สอนสร้าง Cross-Platform App สำหรับ iOS Android Web Desktop 2026

flutter dart cross platform guide
Flutter Dart Cross-Platform Guide 2026
2026-04-09 | tech | 3500 words

ในยุคที่ผู้ใช้งานอยู่บนหลายแพลตฟอร์มทั้ง 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?

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 เปรียบเทียบ

ด้านFlutterReact Native
ภาษาDartJavaScript / TypeScript
RenderingSkia / 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 แล้ว
ขนาดแอปใหญ่กว่าเล็กน้อยเล็กกว่าเล็กน้อย
เลือก Flutter เมื่อ: ต้องการ UI ที่เหมือนกันทุก platform, ต้องการ performance สูง, ต้องการทำ Web + Desktop ด้วย, ทีมพร้อมเรียน Dart
เลือก 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หน้าที่การใช้งาน
providerState Managementจัดการ State แบบง่าย
riverpodState ManagementState Management แบบ compile-safe
flutter_blocState ManagementBLoC pattern
dioHTTP ClientNetwork requests ขั้นสูง
go_routerNavigationDeclarative routing
hiveDatabaseNoSQL local storage
sqfliteDatabaseSQLite local database
freezedCode GenerationImmutable data classes
json_serializableSerializationJSON parsing อัตโนมัติ
cached_network_imageImageCache รูปจาก URL
flutter_svgImageแสดง SVG
intlLocalizationแปลภาษา, format วันที่
flutter_secure_storageSecurityเก็บข้อมูล sensitive
url_launcherUtilityเปิด 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 ที่ใช้ร่วมกัน
Best Practice: ใช้โครงสร้างแบบ Feature-first จะดีกว่า Layer-first เมื่อโปรเจกต์ใหญ่ขึ้น เพราะโค้ดที่เกี่ยวข้องกันอยู่ใกล้กัน ง่ายต่อการดูแลและทดสอบ

สรุป

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 ยังคงลงทุนพัฒนาอย่างต่อเนื่อง เป็นทักษะที่คุ้มค่าที่จะลงทุนเรียนรู้


Back to Blog | iCafe Forex | SiamLanCard | Siam2R