Flutter Widgets — บล็อกพื้นฐานของอินเทอร์เฟซผู้ใช้ในเฟรมเวิร์ก Google Flutter ซึ่งเปิดตัวในเวอร์ชันเสถียร 1.0 ในเดือนธันวาคม 2018 ใน Flutter ทุกอย่างคือวิดเจ็ต: จากหน้าจอและปุ่มไปจนถึงระยะห่างและการจัดตำแหน่ง วิดเจ็ตสร้างเป็นต้นไม้ (widget tree) ซึ่ง Flutter เรนเดอร์บนเอ็นจิน Skia/Impeller ของตัวเอง โดยข้ามคอมโพเนนต์ดั้งเดิมของแพลตฟอร์ม ตามข้อมูลของ Google I/O (2025) มีการใช้ Flutter ในแอปมากกว่า 500,000 รายการบน Google Play และ App Store สถาปัตยกรรม Flutter แบ่งวิดเจ็ตเป็น StatelessWidget (ไม่เปลี่ยนสภาพ) และ StatefulWidget (มีสถานะที่เปลี่ยนแปลงได้ผ่านออบเจ็กต์ State)
ประเด็นสำคัญ
Flutter Widgets — องค์ประกอบ UI ทั้งหมดในเฟรมเวิร์ก Flutter ที่ใช้งานเป็นคลาส Dart ที่ไม่เปลี่ยนสภาพ (immutable) วิดเจ็ตคือการกำหนดค่าขององค์ประกอบ UI: ขนาด สี ตำแหน่ง และตัวจัดการเหตุการณ์ ต่างจาก Android (XML + View) หรือ iOS (Storyboard + UIView) ตรงที่ Flutter ไม่ใช้คอมโพเนนต์ดั้งเดิม — ทุกอย่างถูกวาดโดยเอ็นจิน Skia (Android/iOS/Linux) หรือ Impeller (iOS/macOS) บน Canvas ซึ่งรับประกันลักษณะที่สอดคล้องกันบนทุกแพลตฟอร์มโดยไม่มีความแตกต่างของแพลตฟอร์ม Flutter มาพร้อมกับชุดวิดเจ็ตสองชุด: Material Widgets (Google Material Design 3 สำหรับ Android/เว็บ) และ Cupertino Widgets (Apple HIG สำหรับ iOS/macOS) นักพัฒนาสามารถรวมทั้งสองชุดในแอปพลิเคชันเดียว วิดเจ็ตแต่ละตัวมีเมธอด build() ที่ส่งคืนวิดเจ็ตย่อยหรือองค์ประกอบของวิดเจ็ตเหล่านั้น — นี่คือวิธีสร้างต้นไม้วิดเจ็ต
Flutter ทำงานกับต้นไม้สามต้น: Widget Tree (การกำหนดค่า สร้างโดยนักพัฒนา), Element Tree (การจับคู่วิดเจ็ตกับตำแหน่งในต้นไม้ จัดการโดยเฟรมเวิร์ก) และ Render Tree (ออบเจ็กต์ที่คำนวณเลย์เอาต์และดำเนินการวาด) Widget สร้าง Element ซึ่งสร้าง RenderObject Element ถูกนำกลับมาใช้ใหม่เมื่อการกำหนดค่าเปลี่ยนแปลง (rebuild) — นี่คือกลไกการปรับแต่งประสิทธิภาพของ Flutter: หากชนิดของวิดเจ็ตและ key ตรงกัน Element จะไม่ถูกสร้างใหม่ แต่ถูกอัปเดตด้วยการกำหนดค่าใหม่ การเข้าใจต้นไม้สามต้นเป็นกุญแจสำคัญในการปรับประสิทธิภาพของแอปพลิเคชัน Flutter
StatelessWidget — วิดเจ็ตที่ไม่เก็บสถานะภายใน รับพารามิเตอร์ผ่านคอนสตรักเตอร์และแสดงโดยไม่มีความสามารถในการเปลี่ยนแปลงหลัง build StatelessWidget เหมาะสำหรับองค์ประกอบคงที่: ส่วนหัว ไอคอน อวาตาร์ รายการที่มีข้อมูลตายตัว เมธอด build() ถูกเรียกครั้งเดียวเมื่อสร้างและเมื่อวิดเจ็ตแม่เปลี่ยนแปลง เนื่องจาก StatelessWidget ไม่มีสถานะ จึงไม่ต้องการออบเจ็กต์ State แยกต่างหาก — build() ถูกกำหนดโดยตรงในคลาสวิดเจ็ต
import 'package:flutter/material.dart';
class ProfileHeader extends StatelessWidget {
final String name;
final String avatarUrl;
const ProfileHeader({super.key, required this.name, required this.avatarUrl});
@override
Widget build(BuildContext context) {
return Row(
children: [
CircleAvatar(
radius: 24,
backgroundImage: NetworkImage(avatarUrl),
),
const SizedBox(width: 12),
Text(
name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
);
}
}ProfileHeader — StatelessWidget ที่รับ name และ avatarUrl ผ่านคอนสตรักเตอร์ CircleAvatar แสดงอวาตาร์ Text แสดงชื่อ วิดเจ็ตไม่สามารถเปลี่ยนค่าเหล่านี้หลังการสร้าง การใช้คอนสตรักเตอร์ const (const ProfileHeader) ช่วยให้ Flutter นำอินสแตนซ์วิดเจ็ตกลับมาใช้ใหม่ระหว่างการ rebuild ของแม่ — เป็นการปรับแต่งระดับจุลภาคที่แนะนำสำหรับ StatelessWidget ทั้งหมด
StatefulWidget — วิดเจ็ตที่มีสถานะที่เปลี่ยนแปลงได้เก็บในออบเจ็กต์ State แยกต่างหาก StatefulWidget เองไม่เปลี่ยนสภาพ เช่นเดียวกับ StatelessWidget ส่วนที่เปลี่ยนแปลงได้ถูกมอบหมายให้ State: State คือออบเจ็กต์ที่สร้างครั้งเดียวและคงอยู่หลังการ rebuild หลายครั้งของวิดเจ็ต เมื่อสถานะเปลี่ยนแปลง (setState()) Flutter เรียก build() บน State อัปเดต UI StatefulWidget ใช้สำหรับองค์ประกอบแบบโต้ตอบ: ตัวนับ ฟิลด์ข้อความ กล่องกาเครื่องหมาย แอนิเมชัน ตัวจับเวลา
import 'package:flutter/material.dart';
class LikeButton extends StatefulWidget {
const LikeButton({super.key});
@override
State<LikeButton> createState() => _LikeButtonState();
}
class _LikeButtonState extends State<LikeButton> {
bool liked = false;
int count = 42;
void _toggleLike() {
setState(() {
liked = !liked;
count += liked ? 1 : -1;
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _toggleLike,
child: Text(liked ? '♥ $count' : '♡ $count'),
);
}
}LikeButton — StatefulWidget กับคลาส State _LikeButtonState ฟิลด์ liked และ count เป็นสถานะที่เก็บใน State และไม่ถูกรีเซ็ตเมื่อ rebuild setState() แจ้ง Flutter ว่า UI จำเป็นต้องสร้างใหม่ Flutter ไม่สร้าง State ใหม่ทุกครั้งที่แม่ rebuild — เฉพาะเมื่อการกำหนดค่าเปลี่ยนแปลง (key หรือชนิดวิดเจ็ต) ที่ IT Sectr เราใช้ StatelessWidget สำหรับการแสดงผลและ StatefulWidget สำหรับคอมโพเนนต์แบบโต้ตอบ โดยปฏิบัติตามหลักการลด StatefulWidget ให้อยู่ในขอบเขตของการเปลี่ยนแปลง
Widget Tree — โครงสร้างลำดับชั้นของวิดเจ็ตที่อธิบาย UI ของแอปพลิเคชัน Flutter แปลง Widget Tree เป็น Element Tree: แต่ละ Widget สร้าง Element ซึ่งติดตามตำแหน่งในต้นไม้และการเชื่อมต่อกับ RenderObject Render Tree ดำเนินการเลย์เอาต์ (การกำหนดขนาดและตำแหน่ง) และการวาด (เรนเดอร์) Element เป็นเลเยอร์การปรับแต่งที่สำคัญ: หากชนิดวิดเจ็ตและ key ไม่เปลี่ยนแปลงระหว่างการ rebuild Element จะถูกนำกลับมาใช้ใหม่ อัปเดตการกำหนดค่าโดยไม่สร้าง RenderObject ใหม่ ซึ่งรับประกันประสิทธิภาพของ Flutter ที่อัตราเฟรมสูง (60/120 FPS)
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('วิดเจ็ต Flutter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.flutter_dash, size: 80),
const SizedBox(height: 16),
const Text(
'สวัสดี, Flutter!',
style: TextStyle(fontSize: 24),
),
],
),
),
);
}
}ต้นไม้วิดเจ็ต HomeScreen: Scaffold → AppBar + Center → Column → [Icon, SizedBox, Text] Scaffold คือวิดเจ็ตเลย์เอาต์รากของ Material Design Center จัด Column ย่อยให้อยู่กึ่งกลาง Column จัดเรียงเด็กในแนวตั้งด้วยการจัดกึ่งกลาง (mainAxisAlignment.center) Flutter คำนวณเลย์เอาต์จากบนลงล่าง (ข้อจำกัดถูกส่งจากแม่) และขนาดจากล่างขึ้นบน (sizedByParent หรือการกำหนดขนาดจากเด็ก)
BuildContext — ตัวอธิบายตำแหน่งของวิดเจ็ตใน Element Tree แต่ละเมธอด build() ได้รับ BuildContext ซึ่งวิดเจ็ตสามารถเข้าถึง: MediaQuery (ขนาดหน้าจอ การวางแนว ความหนาแน่นพิกเซล พื้นที่ปลอดภัย), Theme (MaterialThemeData หรือ CupertinoThemeData), Navigator (สแต็กการนำทาง), ScaffoldMessenger (SnackBar), InheritedWidget (การส่งข้อมูลลงในต้นไม้) ไม่ควรบันทึก BuildContext หลังจาก build() เสร็จสิ้นหรือใช้นอกบริบทซิงโครนัส — สิ่งนี้ทำให้เกิดข้อผิดพลาดเช่น “Scaffold.of() called with a context that does not contain a Scaffold” แทนที่จะบันทึก context ให้ใช้วิดเจ็ต Builder หรือรูปแบบ InheritedWidget สำหรับการเข้าถึงข้อมูล
Key — พารามิเตอร์ทางเลือกของคอนสตรักเตอร์ของทุกวิดเจ็ต (super.key) Key ช่วย Flutter ระบุวิดเจ็ตเมื่อลำดับหรือจำนวนรายการในรายการเปลี่ยนแปลง หากไม่มี Key Flutter เปรียบเทียบวิดเจ็ตตามชนิดและตำแหน่ง; เมื่อเพิ่มรายการกลางรายการโดยไม่มี Key Flutter จะนำ Element กลับมาใช้ใหม่สำหรับวิดเจ็ตผิด ทำให้เกิดบักเกี่ยวกับสถานะ (StatefulWidget) และปัญหาแอนิเมชัน ใช้ ValueKey (ตัวระบุไม่ซ้ำ) สำหรับรายการ, ObjectKey (ขึ้นอยู่กับออบเจ็กต์ข้อมูล), UniqueKey (รับประกันไม่ซ้ำในทุก rebuild — สำหรับการสร้างใหม่แบบบังคับ)
Flutter SDK (เวอร์ชันเสถียร 3.3+, Dart 3+) มีวิดเจ็ตในตัวมากกว่า 200 รายการแบ่งเป็นหมวดหมู่: Layout (Row, Column, Stack, Container, Expanded, Flexible, Wrap, GridView, ListView, CustomScrollView), Material Design (Scaffold, AppBar, Card, Drawer, BottomNavigationBar, FloatingActionButton, TabBar, Chip, DataTable), Cupertino (CupertinoPageScaffold, CupertinoNavigationBar, CupertinoButton, CupertinoTabBar), Painting & Effects (Opacity, DecoratedBox, ClipRect, Transform, BackdropFilter, ShaderMask), Async (FutureBuilder, StreamBuilder) และ Accessibility (Semantics, MergeSemantics, ExcludeSemantics) วิดเจ็ตเลย์เอาต์เป็นพื้นฐานของ UI Flutter ทุกชนิด: Row สำหรับลำดับแนวนอน, Column สำหรับแนวตั้ง, Stack สำหรับองค์ประกอบที่ซ้อนกัน, Container กับ BoxDecoration สำหรับการตกแต่ง
| หมวดหมู่ | วิดเจ็ตหลัก | วัตถุประสงค์ |
|---|---|---|
| Layout | Row, Column, Stack, Container, Expanded, Flexible | การวางตำแหน่งและกำหนดขนาดของเด็ก |
| Material | Scaffold, AppBar, Card, Drawer, FAB, TabBar | คอมโพเนนต์ UI Material Design 3 |
| Cupertino | CupertinoPageScaffold, CupertinoNavigationBar, CupertinoButton | คอมโพเนนต์สไตล์ iOS |
| Painting | Opacity, ClipRect, Transform, DecoratedBox, BackdropFilter | เอฟเฟกต์ภาพและการครอบตัด |
| Async | FutureBuilder, StreamBuilder | การโหลดข้อมูลแบบอะซิงโครนัสกับสถานะ UI |
| Accessibility | Semantics, MergeSemantics | มาร์กอัปความหมายสำหรับโปรแกรมอ่านหน้าจอ |
คำถามที่พบบ่อย
StatelessWidget ไม่มีสถานะภายใน — ข้อมูลทั้งหมดถูกส่งผ่านคอนสตรักเตอร์และไม่เปลี่ยนแปลง StatefulWidget เก็บสถานะที่เปลี่ยนแปลงได้ในออบเจ็กต์ State แยกต่างหากซึ่งคงอยู่หลังการ rebuild การเปลี่ยนสถานะเรียก setState() และ Flutter สร้าง UI ใหม่ ใช้วิดเจ็ตที่ไม่เปลี่ยนสภาพสำหรับองค์ประกอบที่ไม่เปลี่ยนแปลง; ใช้ StatefulWidget สำหรับองค์ประกอบแบบโต้ตอบ
BuildContext คือตัวอธิบายตำแหน่งของวิดเจ็ตใน Element Tree ที่ส่งไปยังเมธอด build() ผ่าน context วิดเจ็ตเข้าถึง MediaQuery, Theme, Navigator, ScaffoldMessenger และ InheritedWidget ไม่ควรบันทึก BuildContext หลังจาก build() เสร็จสิ้น สำหรับการเข้าถึงข้อมูล ใช้วิดเจ็ต Builder หรือ InheritedWidget
ใช่ Flutter ใช้ Dart เป็นภาษาโปรแกรมภาษาเดียว Dart เป็นภาษาแบบชนิดคงที่พร้อม null safety, sound null safety, extension methods และ pattern matching การเริ่มต้นใช้พื้นฐาน Dart ก็เพียงพอ: คลาส ฟังก์ชัน async/await คอลเล็กชัน Dart ถูกคอมไพล์เป็นโค้ดดั้งเดิม (AOT) ผ่าน Dart Native และเป็น JavaScript (สำหรับเว็บ) ผ่าน Dart2JS
ไม่ Flutter รองรับหกแพลตฟอร์ม: iOS, Android, Web, macOS, Windows, Linux มากกว่า 90% ของโค้ดถูกใช้ร่วมกันบนทุกแพลตฟอร์ม การปรับเปลี่ยนแพลตฟอร์ม (กล้อง ระบบไฟล์) ถูกใช้งานผ่านปลั๊กอินหรือแชนเนลแพลตฟอร์ม สำหรับเดสก์ท็อป Flutter ใช้เชลล์หน้าต่าง Flutter Desktop; สำหรับเว็บ ใช้เอ็นจิน CanvasKit (WebGL) หรือตัวเรนเดอร์ HTML
Flutter บรรลุ 60/120 FPS ผ่าน: เอ็นจิน 2D ของตัวเอง (Skia/Impeller), การไม่มีบริดจ์ดั้งเดิม (ต่างจาก React Native), ต้นไม้สามต้น (Widget → Element → Render) พร้อมการนำ Element กลับมาใช้ใหม่เมื่อชนิด/key ไม่เปลี่ยนแปลง, อัลกอริธึมเลย์เอาต์แบบทางเดียว (ข้อจำกัดลง ขนาดขึ้น) และ RepaintBoundary สำหรับแยกการวาดซ้ำของพื้นที่แต่ละส่วนของหน้าจอ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ