Flutter คือเฟรมเวิร์ก UI จาก Google สำหรับแอปพลิเคชันข้ามแพลตฟอร์มแบบเนทีฟ ตาม Flutter Docs (2025) มีแอปมากกว่า 500,000 รายการบน Google Play ที่ใช้ Flutter การทำความเข้าใจ Widget, StatefulWidget, BuildContext และทรีการเรนเดอร์ คือพื้นฐานของการพัฒนา Flutter
ประเด็นสำคัญ
Widget — คำอธิบายแบบประกาศของส่วนหนึ่งของ UI ทุกอย่างใน Flutter คือ Widget: จากปุ่มไปจนถึงตัวแอปเอง Widget คือการกำหนดค่า (ไม่ใช่ตัว UI) StatelessWidget — Widget ที่ไม่เปลี่ยนแปลง build() ถูกเรียกหนึ่งครั้ง ไม่มีสถานะ ใช้สำหรับเนื้อหาคงที่ StatefulWidget — Widget ที่เปลี่ยนแปลงได้ มีวัตถุ State ที่สามารถเปลี่ยนแปลงได้ setState() — ทริกเกอร์การสร้างใหม่ State — วัตถุที่เปลี่ยนแปลงได้ซึ่งผูกกับ StatefulWidget มีข้อมูลที่สามารถเปลี่ยนแปลงได้
StatelessWidget — build() ถูกเรียกหนึ่งครั้ง เหมาะสำหรับหัวเรื่อง ไอคอน ข้อความคงที่ StatefulWidget — build() ถูกเรียกทุกครั้งที่เรียก setState() เหมาะสำหรับฟอร์ม รายการที่มีการเปลี่ยนแปลง ตัวจับเวลา InheritedWidget — การส่งข้อมูลลงด้านล่างโดยไม่ต้องส่งผ่านคอนสตรัคเตอร์อย่างชัดเจน Theme, MediaQuery, Navigator — InheritedWidgets BuildContext — ตำแหน่งของ Widget ในทรี context.of<T>, context.watch<T>, context.select<T,R> — การเข้าถึง InheritedWidget
// ตัวอย่าง Flutter Widget: StatefulWidget กับ setState
class CounterWidget extends StatefulWidget {
const CounterWidget({Key? key}) : super(key: key);
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter Counter')),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('You have pushed the button:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _increment,
child: const Icon(Icons.add),
),
);
}
}Widget — การกำหนดค่า StatelessWidget — ไม่เปลี่ยนแปลง StatefulWidget — เปลี่ยนแปลงได้ด้วย setState() IT Sectr แนะนำให้เริ่มต้นด้วย StatelessWidget และเปลี่ยนไปใช้ StatefulWidget เฉพาะเมื่อต้องการสถานะ
Flutter ใช้สามทรี เพื่อการเรนเดอร์ที่มีประสิทธิภาพ Widget Tree — ทรีการกำหนดค่า Widgets ถูกสร้างบ่อยครั้ง (ทุก build) มีน้ำหนักเบา Element Tree — ทรีองค์ประกอบ หนึ่งองค์ประกอบต่อ Widget (หากประเภทไม่เปลี่ยนแปลง) Element — สะพานระหว่าง Widget และ RenderObject RenderObject Tree — ทรีการเรนเดอร์ จัดการขนาด ตำแหน่ง การวาด RenderObject — มีน้ำหนักมาก ถูกสร้างน้อยครั้ง Flutter เปรียบเทียบ Widget Tree และอัปเดต Element Tree ซึ่งทริกเกอร์การอัปเดต RenderObject Tree
Key — ตัวระบุ Widget ใน Element Tree ValueKey, ObjectKey, UniqueKey, PageStorageKey Keys ช่วยให้ Flutter จับคู่ Widget เก่าและใหม่ระหว่างการสร้างใหม่ หากไม่มี Keys Flutter จะจับคู่ตามประเภทและตำแหน่งของ Widget GlobalKey — การเข้าถึง State จาก Widget อื่น ใช้สำหรับ: ฟอร์มที่ซับซ้อน แอนิเมชัน การเข้าถึง State ในการทดสอบ IT Sectr แนะนำให้ใช้ Keys สำหรับรายการ (List.generate) และหลีกเลี่ยง GlobalKey เว้นแต่จำเป็น
Expanded — Widget ที่เติมพื้นที่ว่างใน Row/Column/Flex Flexible — ไม่บังคับให้เต็ม (สามารถเล็กกว่าได้) MediaQuery — InheritedWidget พร้อมข้อมูลอุปกรณ์: ขนาดหน้าจอ การวางแนว แพลตฟอร์ม ปัจจัยสเกลข้อความ Theme/ThemeData — ธีมรวมศูนย์ Theme.of(context) — การเข้าถึง MaterialApp — Widget รากสำหรับการกำหนดค่าเส้นทาง ธีม ตำแหน่งที่ตั้ง Scaffold — เค้าโครงพื้นฐานพร้อม AppBar, Drawer, BottomNavigationBar AppBar — แผงด้านบน
Navigator — สแต็กเส้นทาง (Routes) Navigator.push — เปิดหน้าจอ (คืนค่า Future) Navigator.pop — ปิด (พร้อมผลลัพธ์) MaterialPageRoute — การเปลี่ยนภาพแบบเคลื่อนไหว (เลื่อนจากขวา) Named Routes — ตัวระบุสตริง onGenerateRoute — ตัวจัดการรวมศูนย์ Navigator 2.0 (Router + RouteInformationParser + RouterDelegate) — การนำทางแบบประกาศสำหรับเว็บ การลิงก์ลึก และสถานการณ์ที่ซับซ้อน
Navigator 2.0 — การนำทางแบบประกาศ Router — Widget กลาง RouteInformationParser — การแยกวิเคราะห์ URL RouterDelegate — การจัดการสแต็ก GoRouter (แพ็กเกจ) — ตัวห่อหุ้มแบบง่ายบน Navigator 2.0 รองรับ: เส้นทางที่มีชื่อ พารามิเตอร์ การเปลี่ยนเส้นทาง การลิงก์ลึก IT Sectr แนะนำ GoRouter สำหรับโครงการส่วนใหญ่และ Navigator 2.0 เฉพาะสถานการณ์ที่ซับซ้อน (เว็บ, เดสก์ท็อป)
FutureBuilder — Widget ที่สร้าง UI ตาม Future connectionState: none, waiting, active, done ใช้สำหรับ: คำขอ HTTP, การอ่านฐานข้อมูล, ตัวจับเวลา StreamBuilder — Widget สำหรับ Stream (การไหลของข้อมูลไม่สิ้นสุด) snapshot.data, snapshot.hasError, snapshot.hasData ใช้สำหรับ: WebSocket, Firebase, การอัปเดตตำแหน่ง Widget ทั้งสองจะสร้างใหม่เมื่อได้รับข้อมูลใหม่
Provider — DI ง่าย + การจัดการสถานะ ChangeNotifierProvider, MultiProvider Riverpod — ตัวแทนสมัยใหม่ของ Provider: ปลอดภัยในการคอมไพล์ ไม่ขึ้นกับ BuildContext Bloc — สถาปัตยกรรมที่ขับเคลื่อนด้วยเหตุการณ์: Cubit (เหตุการณ์ง่าย) vs Bloc (ซับซ้อน) GetX — การจัดการสถานะน้ำหนักเบา + DI + การกำหนดเส้นทาง setState — ง่ายที่สุด สำหรับสถานะภายในเครื่อง IT Sectr แนะนำ Riverpod สำหรับโครงการใหม่และ Provider สำหรับโครงการที่มีอยู่
MediaQuery — ขนาดหน้าจอ การวางแนว แพลตฟอร์ม textScaleFactor MediaQuery.of(context).size.width ThemeData — ธีมแอป: สี แบบอักษร รูปร่าง Theme.of(context).textTheme.bodyLarge Hot Reload — การฉีดโค้ดในแอปที่กำลังทำงาน คงสถานะไว้ ไม่รองรับ: การเปลี่ยน main(), ตัวแปรทั่วโลก, ฟิลด์แบบคงที่ Hot Restart — การเริ่มต้นแอปใหม่ทั้งหมด (สูญเสียสถานะ) Hot Reload — ข้อได้เปรียบหลักของ Flutter สำหรับนักพัฒนา
| ประเภท Widget | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| StatelessWidget | ไม่เปลี่ยนแปลง, build หนึ่งครั้ง | Text, Icon, Padding, Center |
| StatefulWidget | เปลี่ยนแปลงได้, setState สร้างใหม่ | TextField, Checkbox, AnimationController |
| InheritedWidget | การส่งข้อมูลลงด้านล่าง | Theme, MediaQuery, Navigator |
| ProxyWidget | ตัวห่อหุ้มสำหรับ Widget อื่น | ParentDataWidget (Positioned, Expanded) |
| RenderObjectWidget | สร้าง RenderObject | Column, Row, Stack (กำหนดเอง) |
StatelessWidget — สำหรับเนื้อหาคงที่ StatefulWidget — สำหรับเนื้อหาแบบไดนามิก InheritedWidget — สำหรับ DI IT Sectr แนะนำ StatelessWidget เป็นค่าเริ่มต้นและ StatefulWidget เฉพาะเมื่อจำเป็น
LayoutBuilder — ปรับตามขนาดของพ่อแม่ OrientationBuilder — ปรับตามการวางแนว Expanded vs Flexible — การกระจายพื้นที่ใน Row/Column MediaQuery — จุดแบ่งสำหรับแท็บเล็ตและเดสก์ท็อป SafeArea — ระยะขอบจากองค์ประกอบระบบ (รอยบาก, แถบสถานะ) AspectRatio — การรักษาสัดส่วน IT Sectr แนะนำ LayoutBuilder สำหรับเค้าโครงที่ปรับเปลี่ยนได้และ OrientationBuilder สำหรับการวางแนวแนวนอน
การทดสอบหน่วย — การทดสอบตรรกะทางธุรกิจ พื้นที่เก็บข้อมูล แบบจำลอง dart test — การเรียกใช้ การทดสอบ Widget — การทดสอบ widget แบบแยกส่วน WidgetTester — pumpWidget, tap, enterText การทดสอบการรวม — สถานการณ์สมบูรณ์บนอุปกรณ์จริง/โปรแกรมจำลอง Mockito — การจำลองการพึ่งพา การทดสอบ Golden — การเปรียบเทียบภาพหน้าจอ (ไฟล์ golden) IT Sectr แนะนำการทดสอบ Widget สำหรับแต่ละหน้าจอและการทดสอบหน่วยสำหรับตรรกะทางธุรกิจ
void main() {
testWidgets('Counter increments on tap', (tester) async {
await tester.pumpWidget(const MaterialApp(home: CounterWidget()));
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byType(FloatingActionButton));
await tester.pump();
expect(find.text('1'), findsOneWidget);
});
}แอนิเมชันโดยนัย — AnimatedContainer, AnimatedOpacity, AnimatedPadding, TweenAnimationBuilder การเปลี่ยนแปลงคุณสมบัติจะเคลื่อนไหวโดยอัตโนมัติ แอนิเมชันชัดแจ้ง — AnimationController พร้อม Tween forward() / reverse() แอนิเมชัน Hero — Hero Widget พร้อมแท็ก — แอนิเมชันการเปลี่ยนภาพระหว่างหน้าจอ แอนิเมชันแบบเหลื่อม — ห่วงโซ่แอนิเมชันด้วย Interval Lottie — การรวมแอนิเมชัน After Effects Rive — แอนิเมชันแบบโต้ตอบ (state machine) IT Sectr แนะนำ AnimatedContainer สำหรับ 80% ของกรณีและ AnimationController สำหรับสถานการณ์ที่ซับซ้อน
class AnimatedBox extends StatefulWidget {
@override
State<AnimatedBox> createState() => _AnimatedBoxState();
}
class _AnimatedBoxState extends State<AnimatedBox> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _expanded = !_expanded),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
color: _expanded ? Colors.blue : Colors.red,
child: const Center(child: Text('Tap me')),
),
);
}
}http — ไคลเอ็นต์ HTTP ง่าย (get, post, put, delete) dio — ไคลเอ็นต์ขั้นสูงพร้อมตัวสกัดกั้น การหมดเวลา การลองใหม่ การบันทึก graphql_flutter — ไคลเอ็นต์ GraphQL สำหรับ Flutter json_serializable — การสร้าง fromJson/toJson ผ่านคำอธิบายประกอบ Retrofit Dart — ไคลเอ็นต์ HTTP แบบ type-safe เช่น Retrofit WebSocket — ผ่าน web_socket_channel Firebase — Cloud Firestore, Realtime Database, Cloud Functions IT Sectr แนะนำ dio สำหรับ REST API และ graphql_flutter สำหรับ GraphQL
Flutter DevTools — ชุดเครื่องมือ: Inspector (ทรี widget), Timeline (ประสิทธิภาพ), Memory, Network, Logging Profile mode — การทำโปรไฟล์โดยไม่ดีบัก Performance overlay — FPS, เวลาสร้าง Flame chart — การติดตามแผนภูมิเปลวไฟ DevTools — เครื่องมือจำเป็นสำหรับการปรับแต่งแอปพลิเคชัน Flutter ในโปรดักชัน
Flutter Web — การคอมไพล์ Flutter เป็น WASM (WebAssembly) CanvasKit (Skia) vs HTML renderer CanvasKit — พิกเซลสมบูรณ์ แต่หนักกว่า HTML renderer — เบา แต่มีข้อจำกัด WASM (Flutter 3.22+) — ประสิทธิภาพแบบเนทีฟ การปรับตัวแบบตอบสนองผ่าน LayoutBuilder และ MediaQuery IT Sectr แนะนำ CanvasKit สำหรับแอปพลิเคชันเว็บเดสก์ท็อปและ HTML renderer สำหรับเว็บไซต์เนื้อหา
คำถามที่พบบ่อย
StatelessWidget — ไม่เปลี่ยนแปลง, build หนึ่งครั้ง StatefulWidget — พร้อม State ที่เปลี่ยนแปลงได้และ setState()
Widget Tree — การกำหนดค่า Element Tree — สะพาน RenderObject Tree — การเรนเดอร์ (ขนาด, การวาด)
BuildContext — ตำแหน่ง Widget ในทรี InheritedWidget — การส่งข้อมูลลงด้านล่าง (Theme, MediaQuery)
Navigator — สแต็กหน้าจอ push() → pop() Named Routes — ชื่อสตริง Navigator 2.0 — แบบประกาศ
Hot Reload — การฉีดโค้ดใน 1-2 วินาที การคงสถานะไว้ Hot Restart — การเริ่มต้นใหม่ทั้งหมด
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ