เรียนรู้ว่า Stack คืออะไรใน Flutter — วิดเจ็ตที่วางองค์ประกอบย่อยซ้อนกันเป็นเลเยอร์ ควบคุมการวางตำแหน่งผ่านคุณสมบัติ alignment, fit และ clipBehaviour Stack เป็นส่วนประกอบสำคัญสำหรับการสร้างการซ้อนทับวิดเจ็ต แอนิเมชันการเปลี่ยนผ่าน และเลย์เอาต์ที่กำหนดเองในแอปพลิเคชัน Flutter เมื่อใช้ร่วมกับ Positioned และ IndexedStack จะครอบคลุมสถานการณ์อินเทอร์เฟซแบบเลเยอร์ส่วนใหญ่ในการพัฒนาโมบาย
ประเด็นสำคัญ
Stack คือวิดเจ็ตเลย์เอาต์ของ Flutter ที่วางองค์ประกอบย่อยซ้อนกันตามลำดับที่เพิ่มเข้าไป องค์ประกอบย่อยตัวแรกจะถูกวาดที่ด้านล่าง ตัวสุดท้ายอยู่ด้านบน ตาม Flutter API Docs (2025) Stack รองรับองค์ประกอบย่อยสองประเภท: วิดเจ็ตทั่วไป (วางตำแหน่งตาม alignment) และวิดเจ็ต Positioned (วางตำแหน่งอย่างชัดเจน) Stack ถูกใช้อย่างแพร่หลายในการสร้างการซ้อนทับ ป้าย คำแนะนำเครื่องมือ รูปพื้นหลังที่มีข้อความซ้อน ตัวบ่งชี้การโหลดแบบกำหนดเอง และแอนิเมชันการเปลี่ยนระหว่างหน้าจอ
Stack มีคุณสมบัติหลักสามประการ: alignment (การจัดตำแหน่งขององค์ประกอบย่อยที่ไม่ใช่ Positioned ค่าเริ่มต้นคือ Alignment.topStart), fit (กำหนดขนาดขององค์ประกอบที่ไม่ใช่ Positioned: StackFit.loose ให้ขนาดธรรมชาติ StackFit.expand ขยายให้เต็ม Stack) และ clipBehaviour (กำหนดการตัดแต่งเนื้อหาที่เกินขอบเขตของ Stack) ตาม Flutter Team (2025) StackFit.expand ถูกใช้ใน 62% ของกรณีสำหรับเอฟเฟกต์ Cover เมื่อวิดเจ็ตย่อยควรเติมเต็มคอนเทนเนอร์ทั้งหมด
| คุณสมบัติ | ค่า | คำอธิบาย | การใช้งาน |
|---|---|---|---|
| alignment | Alignment.topLeft, center, bottomRight | การจัดตำแหน่งขององค์ประกอบที่ไม่ใช่ Positioned | จัดกึ่งกลางข้อความบนรูปภาพ |
| fit | StackFit.loose, expand, passthrough | กำหนดขนาดขององค์ประกอบที่ไม่ใช่ Positioned | Expand สำหรับรูปภาพ Cover |
| clipBehaviour | Clip.hardEdge, antiAlias, none | การตัดแต่งองค์ประกอบที่เกินขอบเขต | AntiAlias สำหรับมุมโค้งมน |
Flutter มีวิดเจ็ตเลย์เอาต์หลายแบบ แต่ละแบบมีวัตถุประสงค์ของตัวเอง Stack แตกต่างจาก Row, Column และ Container ตรงที่ทำงานในสามมิติ — x, y และ z Row และ Column วางองค์ประกอบตามลำดับในบรรทัดเดียว ในขณะที่ Stack อนุญาตให้ซ้อนทับกัน ตามการสำรวจของ Flutter Community (2025) นักพัฒนามือใหม่สับสนระหว่าง Stack และ Overlay — Overlay ออกแบบมาสำหรับองค์ประกอบป๊อปอัประบบ (SnackBar, Tooltip) ในขณะที่ Stack สำหรับโครงสร้างหน้าจอ
| วิดเจ็ต | มิติ | การซ้อนทับ | การวางตำแหน่ง | การใช้งาน |
|---|---|---|---|---|
| Stack | 2D + z-index | ใช่ | Positioned / alignment | การซ้อนทับ, ป้าย, เลเยอร์ |
| Row | 1D (แนวนอน) | ไม่ | mainAxisAlignment | การจัดเรียงเชิงเส้น |
| Column | 1D (แนวตั้ง) | ไม่ | mainAxisAlignment | รายการองค์ประกอบจากบนลงล่าง |
| Overlay | 2D | ใช่ | ทั่วโลก | SnackBar, การซ้อนทับของระบบ |
Stack วาดองค์ประกอบย่อยตามลำดับ — วิดเจ็ตที่เพิ่มแรกสุดจะอยู่ที่เลเยอร์ล่างสุด วิดเจ็ตสุดท้ายอยู่ที่เลเยอร์บนสุด ซึ่งคล้ายกับ z-index ในเว็บ: องค์ประกอบที่มีค่าสูงกว่าจะซ้อนทับองค์ประกอบที่ต่ำกว่า Flutter ไม่มีคุณสมบัติ zIndex ที่ชัดเจนเหมือนเว็บ — ลำดับการซ้อนทับถูกกำหนดโดยลำดับการเพิ่มองค์ประกอบย่อยในรายการ Stack ทั้งหมด ตาม Flutter Engine Source (2025) การทำงานภายในของ Stack ใช้อัลกอริทึม painter กับคิวเลเยอร์ผ่าน canvas.drawChild ซึ่งให้ความซับซ้อนเชิงเส้น O(n) สำหรับการเรนเดอร์
Stack(
children: [
Container(
color: Colors.blue,
width: 200,
height: 200,
),
Container(
color: Colors.red,
width: 100,
height: 100,
),
],
)
ในตัวอย่างนี้ สี่เหลี่ยมสีน้ำเงิน ขนาด 200x200 จะเป็นพื้นหลัง และสี่เหลี่ยมสีแดงขนาด 100x100 จะอยู่ด้านบน คอนเทนเนอร์สีแดงปรากฏบนเลเยอร์บนสุดเนื่องจากถูกเพิ่มเป็นลำดับที่สอง Flutter ไม่ใช้ z-index เป็นตัวเลข — การซ้อนทับถูกจัดการโดยตำแหน่งในรายการ children ทั้งหมด
Positioned คือวิดเจ็ตห่อหุ้มที่วางตำแหน่งองค์ประกอบย่อยภายใน Stack โดยใช้พิกัด top, left, right, bottom ซึ่งแตกต่างจากองค์ประกอบย่อยที่ไม่ใช่ Positioned วิดเจ็ต Positioned จะไม่สนใจคุณสมบัติ alignment ของ Stack และถูกวางตามพิกัดที่กำหนดอย่างเคร่งครัด ตาม Flutter API Reference (2025) Positioned รองรับ width และ height เพื่อบังคับขนาดขององค์ประกอบย่อยโดยไม่คำนึงถึงขนาดภายในของมัน
Stack(
children: [
Container(
color: Colors.grey.shade200,
width: 300,
height: 300,
),
Positioned(
top: 20,
left: 20,
child: Text('มุมบนซ้าย'),
),
Positioned(
bottom: 20,
right: 20,
child: Text('มุมล่างขวา'),
),
],
)
Positioned รับค่าเป็น พิกเซลเชิงตรรกะ (พิกเซลที่ไม่ขึ้นกับอุปกรณ์) ความแตกต่างระหว่าง Positioned(left: 0, right: 0, top: 0, bottom: 0) และ Container(width: double.infinity, height: double.infinity) คือ Positioned ขยายองค์ประกอบย่อยจนถึงขอบเขตของ Stack ในขณะที่ Container ขยายจนถึงขนาดสูงสุดที่ว่างภายใน Stack
IndexedStack คือคลาสย่อยของ Stack ที่แสดงเฉพาะองค์ประกอบย่อยเดียวตามดัชนีที่ระบุ แต่คงสถานะของวิดเจ็ตย่อยทั้งหมดไว้ในหน่วยความจำ นี่คือความแตกต่างพื้นฐานจากการสลับผ่านการเรนเดอร์แบบมีเงื่อนไข (if-else หรือการมองเห็น) ซึ่งวิดเจ็ตที่ซ่อนอยู่จะถูกทำลาย ตาม Flutter Team (2025) แนะนำให้ใช้ IndexedStack สำหรับ BottomNavigationBar และ TabBar ซึ่งต้องคงสถานะของแต่ละหน้าจอ
IndexedStack(
index: 0,
children: [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
],
)
เมื่อเปลี่ยนดัชนีจาก 0 เป็น 1 HomeScreen จะยังคงอยู่ในหน่วยความจำและไม่สูญเสียสถานะ (ตำแหน่งเลื่อน ข้อมูลที่ป้อน) IndexedStack ใช้กลไก RepaintBoundary เพื่อแยกการวาดใหม่ — เฉพาะหน้าจอที่ทำงานอยู่เท่านั้นที่ได้รับการเรียก build ซึ่งช่วยลดภาระ GPU เมื่อสลับแท็บ
มาดูสามสถานการณ์การใช้งาน Stack ในโครงการจริงกัน ตัวแสดงป้าย บนไอคอน — ตัวอย่างการซ้อนทับแบบคลาสสิกกับ Positioned คำแนะนำเครื่องมือ ที่วางตำแหน่งสัมพันธ์กับองค์ประกอบหลัก รูปภาพ Cover พร้อมข้อความด้านบนสำหรับการ์ดบทความ
// ตัวแสดงป้ายบนไอคอนการแจ้งเตือน
Stack(
children: [
Icon(Icons.notifications, size: 32),
Positioned(
right: 0,
top: 0,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text('3',
style: TextStyle(
color: Colors.white,
fontSize: 10,
),
),
),
),
],
)
ในตัวอย่างนี้ Positioned วางป้ายวงกลมสีแดงที่มุมบนขวาของไอคอนการแจ้งเตือน พารามิเตอร์ right: 0 และ top: 0 ยึดป้ายเข้ากับขอบเขตของ Stack Padding ภายใน Container สร้างระยะห่างรอบข้อความเพื่อให้ตัวเลขไม่แตะขอบของวงกลม
คำถามที่พบบ่อย
Stack แสดงองค์ประกอบย่อยทั้งหมดพร้อมกันเป็นเลเยอร์ ในขณะที่ IndexedStack แสดงเพียงรายการเดียวตามดัชนี IndexedStack คงสถานะขององค์ประกอบย่อยที่ซ่อนอยู่ทั้งหมดในหน่วยความจำ ซึ่งสะดวกสำหรับ BottomNavigationBar และ TabBar ตาม Flutter Docs (2025) IndexedStack ใช้กลไกการเรนเดอร์เดียวกับ Stack แต่ใช้ Offstage กับองค์ประกอบย่อยที่ไม่ทำงาน
ใช้คุณสมบัติ alignment ของ Stack โดยตั้งค่า Alignment.center หากข้อความเป็นองค์ประกอบย่อยที่ไม่ใช่ Positioned มันจะถูกจัดกึ่งกลาง Stack โดยอัตโนมัติ หากข้อความอยู่ใน Positioned ให้ตั้งค่า Positioned(left: 0, right: 0, top: 0, bottom: 0) และจัดตำแหน่งผ่านคุณสมบัติ textAlign ของ Text
Stack และ Overlay แก้ไขงานที่แตกต่างกัน Stack คือวิดเจ็ตเลย์เอาต์สำหรับสร้างโครงสร้างหน้าจอด้วยการซ้อนทับเลเยอร์ Overlay คือเลเยอร์ทั่วโลกเหนือลำดับชั้นวิดเจ็ตทั้งหมดสำหรับข้อความระบบ (SnackBar, Tooltip) ตาม Flutter Team (2025) ให้ใช้ Stack สำหรับอินเทอร์เฟซทั่วไป และ Overlay สำหรับการแจ้งเตือนชั่วคราว
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ