Theme และ ThemeData — กลไกหลักของการสร้างธีมใน Flutter ที่ช่วยให้กำหนดสี ฟอนต์ ระยะขอบ และสไตล์ของคอมโพเนนต์ในระดับโลกสำหรับทั้งแอปพลิเคชัน ThemeData เป็นออบเจกต์การกำหนดค่าที่ส่งไปยัง MaterialApp ผ่านพารามิเตอร์ theme ตามเอกสารทางการของ Flutter (2026) การกำหนดค่า ThemeData ที่ถูกต้องจะลดโค้ดสไตล์ที่ซ้ำซ้อนลง 70–80% และรับประกันรูปลักษณ์ที่สม่ำเสมอในทุกหน้าจอ ระบบสืบทอดในตัวช่วยให้สามารถแทนที่ธีมในระดับวิดเจ็ตแต่ละตัวผ่าน Theme.of(context)
ประเด็นสำคัญ
ThemeData เป็นคลาสที่ประกอบด้วยคำอธิบายที่สมบูรณ์ของสไตล์ภาพของแอปพลิเคชัน Material มันถูกส่งไปยัง MaterialApp ผ่านพารามิเตอร์ theme หรือ darkTheme และถูกสืบทอดโดยวิดเจ็ตลูกทั้งหมด ทุกวิดเจ็ต Material Design ใช้ ThemeData เพื่อรับสไตล์ของมัน: สีของปุ่ม ขนาดของ AppBar สไตล์ข้อความ ระยะขอบของการ์ด และไอคอน
ที่ศูนย์กลางของ ThemeData คือ ColorScheme ซึ่งกำหนดสี primary, secondary, surface, error และสีอื่น ๆ จากสีเหล่านี้ สถานะ (hover, focus, pressed) จะถูกคำนวณโดยอัตโนมัติสำหรับคอมโพเนนต์ Material ทั้งหมด ตัวอย่างเช่น ElevatedButton ใช้สี primary สำหรับพื้นหลังและ onPrimary สำหรับข้อความ — โดยไม่ต้องกำหนดค่าเพิ่มเติม
วิดเจ็ต Theme เป็น InheritedWidget ที่เผยแพร่ธีมลงไปในลำดับชั้นของวิดเจ็ต วิดเจ็ตใด ๆ สามารถเข้าถึงธีมปัจจุบันผ่าน Theme.of(context) และยังสามารถสร้างธีมใหม่โดยอิงจากธีมที่มีอยู่ด้วยค่าที่ถูกแทนที่ผ่าน Theme.copyWith หรือ ThemeData.copyWith
สรุป: ThemeData คือแหล่งที่มาเดียวของสไตล์แอปพลิเคชัน เริ่มต้นการพัฒนาโดยกำหนด ThemeData — สิ่งนี้กำหนดภาษาภาพของทั้งโปรเจกต์และป้องกันสไตล์ที่กระจัดกระจายในหน้าจอต่าง ๆ
ColorScheme เป็นองค์ประกอบหลักของการสร้างธีมใน Material 3 มันแทนที่ primarySwatch และ primaryColor ที่ล้าสมัย โดยให้ชุดสีที่สมบูรณ์สำหรับทุกสถานะและคอมโพเนนต์
ColorScheme ประกอบด้วย 12 สีหลัก: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline และ shadow สำหรับแต่ละสี รูปแบบสว่างและมืดจะถูกคำนวณโดยอัตโนมัติ รวมถึงสีสำหรับสถานะ hover, focus, pressed และ dragged เมื่อระบุเฉพาะ primary เฟรมเวิร์กจะสร้างสีที่เหลือโดยอัตโนมัติผ่าน ColorScheme.fromSeed
เมธอด ColorScheme.fromSeed (Material 3) รับสีฐาน seedColor และสร้างจานสีที่กลมกลืนสมบูรณ์โดยอิงตามอัลกอริทึม Material Design 3 สิ่งนี้ช่วยขจัดความจำเป็นในการเลือกสีเสริมและสีคล้ายคลึงด้วยตนเอง เพียงส่งสีหลักของแบรนด์หนึ่งสี ColorScheme.fromSeed จะสร้างจานสีที่สอดคล้องกันสำหรับทั้งระบบ Material
สรุป: ในโปรเจกต์ใหม่ ใช้ ColorScheme.fromSeed เพื่อสร้างจานสี สำหรับความเข้ากันได้กับ Material 2 — ใช้ primarySwatch ColorScheme เป็นขั้นต่ำที่จำเป็นสำหรับ Material 3
TextTheme เป็นส่วนหนึ่งของ ThemeData ที่กำหนดสไตล์ข้อความสำหรับทุกระดับหัวเรื่อง หัวเรื่องย่อย เนื้อหาหลัก และป้ายกำกับ Material Design กำหนด 15 สไตล์ข้อความ: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium และอื่น ๆ
การจัดเรียงตัวอักษรถูกกำหนดผ่าน GoogleFonts สำหรับการโหลดฟอนต์อัตโนมัติ หรือผ่าน Theme.of(context).textTheme ในตัวกับออบเจกต์ TextStyle ที่กำหนดเอง GoogleFonts ให้ฟอนต์หลายร้อยแบบผ่านแพ็คเกจ google_fonts ซึ่งจัดเก็บไว้ในแคชภายในหลังจากการโหลดครั้งแรก ทางเลือกอื่นคือการฝังฟอนต์ในสินทรัพย์ของโปรเจกต์ผ่าน pubspec.yaml
สำหรับการจัดเรียงตัวอักษรแบบปรับได้ TextTheme รองรับ MediaQuery.textScaleFactor — เมื่อขนาดฟอนต์ระบบเพิ่มขึ้น สไตล์ข้อความทั้งหมดจะถูกปรับขนาดตามสัดส่วน นอกจากนี้ สามารถกำหนดขนาดฟอนต์ที่แตกต่างกันสำหรับฟอร์มแฟคเตอร์ต่าง ๆ ผ่าน isTest ขึ้นอยู่กับ MediaQuery.size.width
สรุป: กำหนด TextTheme ใน ThemeData และใช้ Theme.of(context).textTheme สำหรับองค์ประกอบข้อความทั้งหมด สิ่งนี้รับประกันความสอดคล้องของฟอนต์และทำให้การเปลี่ยนแปลงการจัดเรียงตัวอักษรในระดับโลกง่ายขึ้น
Theme เป็น InheritedWidget ที่เผยแพร่ธีมจากพ่อแม่ไปยังลูก วิดเจ็ตลูกใด ๆ สามารถอ่านธีมปัจจุบันผ่าน Theme.of(context) และสร้างสำเนาของตัวเองด้วยการเปลี่ยนแปลงผ่าน ThemeData.copyWith
กลไกการสืบทอด: หากวิดเจ็ตถูกห่อด้วย Theme ที่มีข้อมูลใหม่ ผู้สืบทอดทั้งหมดจะเห็นธีมที่ถูกแทนที่ ในขณะที่วิดเจ็ตภายนอกตัวห่อนี้จะเห็นธีมดั้งเดิม สิ่งนี้ช่วยให้ เช่น แยกสไตล์สำหรับหน้าจอเฉพาะ: ส่วนหัวใช้ธีมกำหนดเองกับ AppBarTheme ที่ถูกแก้ไข ในขณะที่แอปพลิเคชันที่เหลือใช้ธีมมาตรฐาน
การซ้อนกันของ Theme สามารถไม่มีขีดจำกัด Flutter ใช้การค้นหาแบบลำดับชั้น: เมื่อเรียก Theme.of(context) เฟรมเวิร์กจะปีนขึ้นไปในลำดับชั้นจนถึง Theme แรกและคืนค่าธีมของมัน หากไม่พบ Theme ใด — จะมีการโยนข้อยกเว้น ดังนั้น MaterialApp จะห่อแอปพลิเคชันใน Theme เสมอ
สรุป: การสืบทอด Theme เป็นกลไกที่ยืดหยุ่นสำหรับการแยกสไตล์ ใช้การแทนที่ธีมสำหรับหน้าจอแต่ละหน้าโดยไม่ทำซ้ำการกำหนดค่าโดยรวม
ธีมมืด คือความสามารถในตัวของ Flutter ในการสลับระหว่างธีมสว่างและมืด ThemeData ถูกเก็บไว้ในพารามิเตอร์ theme (สว่าง) และ darkTheme (มืด) ของ MaterialApp พารามิเตอร์ themeMode ควบคุมโหมด: ThemeMode.light (สว่างเสมอ), ThemeMode.dark (มืดเสมอ) หรือ ThemeMode.system (ตามการตั้งค่าระบบ)
เมื่อใช้งานธีมมืด ไม่จำเป็นต้องสร้างสไตล์ทั้งหมดขึ้นมาใหม่ เพียงส่ง ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) ไปยัง darkTheme เฟรมเวิร์กจะปรับสีโดยอัตโนมัติสำหรับพื้นหลังมืด: surface กลายเป็นสีเทาเข้ม ข้อความกลายเป็นสว่าง ธีมย่อยอื่น ๆ (AppBarTheme, CardTheme, BottomNavigationBarTheme) ถูกสืบทอดจากเวอร์ชันสว่างเว้นแต่จะถูกแทนที่อย่างชัดเจน
สำหรับการเปลี่ยนระหว่างธีมอย่างราบรื่น Flutter มีแอนิเมชันผ่าน AnimatedTheme เมื่อ themeMode เปลี่ยนไป สีและสไตล์จะเคลื่อนไหว สร้างการเปลี่ยนที่สวยงาม AnimatedTheme ใช้ Duration และ Curve เพื่อกำหนดค่าความเร็วของแอนิเมชัน
สรุป: ธีมมืดถูกใช้งานผ่าน darkTheme และ ColorScheme.fromSeed ด้วย brightness: Brightness.dark ใช้ ThemeMode.system เพื่อตามการตั้งค่าระบบของผู้ใช้
ตัวอย่างที่ 1 — การกำหนดค่าธีมระดับโลกผ่าน MaterialApp โดยใช้ ColorScheme.fromSeed
final ThemeData appTheme = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.light,
),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
cardTheme: const CardTheme(
elevation: 2,
margin: EdgeInsets.all(8),
),
);
return MaterialApp(
theme: appTheme,
themeMode: ThemeMode.system,
home: const MyHomePage(),
);
ตัวอย่างนี้สร้าง ThemeData ด้วย Material 3 โครงสร้างสีที่อิงจากสีคราม และสไตล์ AppBar และ Card ที่กำหนดเอง useMaterial3 เปิดใช้งานระบบธีม Material 3 ใหม่ และ ColorScheme.fromSeed สร้างจานสีที่สมบูรณ์
ตัวอย่างที่ 2 — การเข้าถึงธีมและการแทนที่สำหรับส่วนหนึ่งของแอปพลิเคชัน
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
children: [
Text(
'Standard headline',
style: theme.textTheme.headlineMedium,
),
Theme(
data: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.orange,
),
),
child: const SpecialSection(),
),
],
);
}
ตัวอย่างที่สองแสดงการใช้ Theme.of(context) เพื่ออ่านสไตล์และ Theme() กับ copyWith เพื่อแทนที่ธีมในส่วนหนึ่งของลำดับชั้นโดยไม่เปลี่ยนการกำหนดค่าโดยรวม SpecialSection ภายใน Theme จะได้รับสี primary สีส้ม ในขณะที่แอปพลิเคชันที่เหลือคงสีครามเดิม
สรุป: ธีมระดับโลกถูกตั้งค่าใน MaterialApp เข้าถึงผ่าน Theme.of(context) และแทนที่ผ่าน Theme ด้วย copyWith สำหรับ Material 3 ใช้ ColorScheme.fromSeed
ข้อผิดพลาด 1: การใช้ primarySwatch แทน ColorScheme ใน Material 3 primarySwatch ล้าสมัยและไม่รองรับใน Material 3 เมื่อใช้ useMaterial3: true กับ primarySwatch Flutter จะแสดงคำเตือน วิธีแก้: เปลี่ยนไปใช้ ColorScheme.fromSeed สำหรับการสร้างจานสี
ข้อผิดพลาด 2: การกำหนดสีตายตัวในวิดเจ็ตแทนการใช้ Theme.of(context) นักพัฒนาเขียน Colors.blue โดยตรงใน Container แทนการใช้สี primary หรือ secondary จากธีม เมื่อธีมเปลี่ยนไป สีเหล่านี้ยังคงไม่เปลี่ยนแปลง ทำลายความสอดคล้องของสไตล์ วิธีแก้: ใช้ Theme.of(context).colorScheme.primary หรือสีอื่น ๆ จากธีมเสมอ
ข้อผิดพลาด 3: การไม่มี darkTheme หากแอปพลิเคชันไม่ได้กำหนด darkTheme เมื่อเปิดธีมมืดบนอุปกรณ์ Flutter จะพยายามปรับธีมสว่างโดยอัตโนมัติ ซึ่งมักให้ผลลัพธ์ที่มีคุณภาพต่ำ วิธีแก้: ส่ง darkTheme ไปยัง MaterialApp เสมอ แม้ว่าธีมมืดจะไม่ใช่สิ่งสำคัญ — นี่คือ UX ที่สำคัญสำหรับผู้ใช้
ข้อผิดพลาด 4: การสร้างธีมใหม่ทุกครั้งที่ build หาก ThemeData ถูกสร้างขึ้นภายในเมธอด build การสร้างวิดเจ็ตใหม่แต่ละครั้งจะสร้างออบเจกต์ธีมใหม่ ทำลายการเพิ่มประสิทธิภาพของ InheritedWidget วิธีแก้: ย้าย ThemeData ไปยัง ฟิลด์ static หรือคอนสตรัคเตอร์ const ภายนอก build
สรุป: การสร้างธีมที่ถูกต้องต้องการวิธีการแบบรวมศูนย์ ย้าย ThemeData ไปยังค่าคงที่ ใช้โครงสร้างสีจากธีม และกำหนดทั้งสองธีม — สว่างและมืด — เสมอ
คำถามที่พบบ่อย
ThemeData คือข้อมูลการกำหนดค่า (สี ฟอนต์ สไตล์) Theme คือ InheritedWidget ที่ส่ง ThemeData ลงไปในลำดับชั้นของวิดเจ็ต Theme.of(context) คืนค่า ThemeData ที่ Theme ใกล้ที่สุดเผยแพร่ในบริบทปัจจุบัน
ควบคุม themeMode ใน MaterialApp ผ่านสถานะแอปพลิเคชัน เปลี่ยนสถานะจาก ThemeMode.light เป็น ThemeMode.dark แล้ว Flutter จะสร้างลำดับชั้นทั้งหมดใหม่ด้วยธีมใหม่ สำหรับแอนิเมชันที่ราบรื่น ใช้ AnimatedTheme
ColorScheme.fromSeed เป็นเมธอด Material 3 ที่รับสีฐานหนึ่งสี (seedColor) และสร้างจานสีที่กลมกลืนสมบูรณ์: primary, secondary, tertiary, surface, error และรูปแบบ on ของมัน ช่วยให้ได้โครงสร้างสีที่สอดคล้องกันจากสีแบรนด์เดียว
ผ่าน TextTheme ภายใน ThemeData: ระบุ TextStyle พร้อม fontFamily สำหรับแต่ละระดับ สำหรับ Google Fonts ใช้แพ็คเกจ google_fonts ส่ง fontFamily โดยตรงไปยัง TextTheme สำหรับฟอนต์ภายใน — รวมไว้ผ่าน pubspec.yaml ในส่วน fonts
ได้ ห่อหน้าจอใน Theme ด้วยข้อมูลที่ถูกแทนที่ผ่าน copyWith วิดเจ็ตลูกทั้งหมดภายใน Theme นี้จะได้รับสไตล์ที่ถูกแก้ไข ในขณะที่แอปพลิเคชันที่เหลือคงธีมเดิมไว้ สิ่งนี้ไม่สร้างภาระเพิ่มเติม
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ