สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท
ปรับชุดสีให้เข้ากับวอลเปเปอร์ของผู้ใช้ และสลับระหว่างโหมดสว่างกับมืดได้อย่างราบรื่น
สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท เป็นบทเรียน Flutter Mobile Development ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Flutter Mobile Development และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Flutter Mobile Development มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
Why Dynamic Color Matters
Material 3 introduced dynamic color: an app's palette can be derived from the user's wallpaper (Android 12+) so your UI feels personal and native to the device.
- On supported devices, the system exposes a
ColorSchemesourced from the wallpaper. - On older devices or other platforms, you fall back to a brand seed color.
- The same logic must produce both a light and a dark scheme.
In this lesson you'll wire up dynamic color, build adaptive light/dark themes, and let the system decide which one to show.
Seed Color as the Foundation
Even with dynamic color, you always need a fallback. Material 3's ColorScheme.fromSeed generates a full, accessible palette from a single seed color.
- Pass
brightnessto get the matching light or dark variant. - The generated scheme guarantees correct contrast between roles like
primaryandonPrimary.
import 'package:flutter/material.dart';
final ColorScheme lightScheme = ColorScheme.fromSeed(
seedColor: const Color(0xFF6750A4),
brightness: Brightness.light,
);
final ColorScheme darkScheme = ColorScheme.fromSeed(
seedColor: const Color(0xFF6750A4),
brightness: Brightness.dark,
);Building Light and Dark ThemeData
A ThemeData wraps a ColorScheme plus typography and component styles. With Material 3 you set useMaterial3: true and feed in the scheme.
- Create one
ThemeDataper brightness. - Keep them consistent by deriving both from the same seed.
ThemeData buildTheme(ColorScheme scheme) {
return ThemeData(
useMaterial3: true,
colorScheme: scheme,
appBarTheme: AppBarTheme(
backgroundColor: scheme.surface,
foregroundColor: scheme.onSurface,
),
);
}Letting the System Pick the Mode
MaterialApp accepts both a theme (light) and a darkTheme. The themeMode property decides which is active.
ThemeMode.systemfollows the OS setting automatically.ThemeMode.light/ThemeMode.darkforce a mode (useful for an in-app toggle).
With ThemeMode.system, Flutter switches seamlessly when the user flips dark mode in their device settings.
MaterialApp(
theme: buildTheme(lightScheme),
darkTheme: buildTheme(darkScheme),
themeMode: ThemeMode.system,
home: const HomePage(),
);Reading Dynamic Color from the System
The dynamic_color package exposes the wallpaper-based palette via the DynamicColorBuilder widget. Its builder gives you optional light and dark ColorSchemes.
- If the device supports dynamic color, both schemes are non-null.
- If not, they are
nulland you fall back to your seed.
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart';
DynamicColorBuilder(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
final light = lightDynamic ?? lightScheme;
final dark = darkDynamic ?? darkScheme;
return MaterialApp(
theme: buildTheme(light),
darkTheme: buildTheme(dark),
themeMode: ThemeMode.system,
home: const HomePage(),
);
},
);Harmonizing Brand Colors
When you blend dynamic wallpaper colors with your brand color, the two can clash. The harmonize helpers shift a custom color toward the dynamic primary so accents feel cohesive.
- Use
harmonizeWithon aColoragainst the scheme's primary. - This keeps custom semantic colors (success, warning) on-brand with the wallpaper.
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart';
ColorScheme harmonizeScheme(ColorScheme scheme) {
return scheme.harmonized();
}
Color harmonizeBrand(Color brand, ColorScheme scheme) {
return brand.harmonizeWith(scheme.primary);
}Color Roles, Not Hard-Coded Hex
For themes to adapt, widgets must read semantic roles from Theme.of(context).colorScheme instead of hard-coded colors.
scheme.primarywithscheme.onPrimaryfor prominent actions.scheme.surfacewithscheme.onSurfacefor backgrounds and text.scheme.surfaceContainerHighestfor subtle cards.
Because these roles flip with brightness, your widgets become light/dark agnostic automatically.
Widget buildCard(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Container(
color: scheme.surfaceContainerHighest,
child: Text(
'Adaptive',
style: TextStyle(color: scheme.onSurface),
),
);
}Pure Dart: Picking a Scheme by Brightness
The decision logic for which scheme to use is plain Dart. Here is a tiny, runnable model of the fallback rule: prefer the dynamic scheme, otherwise use the seed-derived one.
- This mirrors the
lightDynamic ?? lightSchemepattern. - No Flutter widgets needed — just the selection rule.
enum Brightness { light, dark }
String chooseScheme({
required Brightness brightness,
required bool supportsDynamic,
}) {
final base = supportsDynamic ? 'dynamic' : 'seed';
return '$base-${brightness.name}';
}
void main() {
print(chooseScheme(brightness: Brightness.dark, supportsDynamic: true));
print(chooseScheme(brightness: Brightness.light, supportsDynamic: false));
}An In-App Theme Toggle
Users often want to override the system. Store a ThemeMode in state and pass it to MaterialApp. A ValueNotifier is a lightweight way to rebuild on change.
- Default to
ThemeMode.systemso wallpaper/dark-mode still apply. - Let the toggle pick light, dark, or back to system.
final themeModeNotifier = ValueNotifier<ThemeMode>(ThemeMode.system);
ValueListenableBuilder<ThemeMode>(
valueListenable: themeModeNotifier,
builder: (context, mode, _) {
return MaterialApp(
theme: buildTheme(lightScheme),
darkTheme: buildTheme(darkScheme),
themeMode: mode,
home: const HomePage(),
);
},
);Reacting to the Effective Brightness
Inside widgets you sometimes need to know which mode is actually rendering (for example, to pick a different illustration). Read it from the active theme, not from the device, because an in-app toggle may override the system.
Theme.of(context).brightnessreflects the resolved theme.- Avoid
MediaQuery.platformBrightnessOfwhen a toggle can override it.
Widget heroImage(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Image.asset(
isDark ? 'assets/hero_dark.png' : 'assets/hero_light.png',
);
}Testing Both Modes Quickly
Always verify your UI in both schemes before shipping. Two practical habits:
- Toggle dark mode in the device/emulator settings to confirm
ThemeMode.systemreacts. - Temporarily force
themeMode: ThemeMode.darkto catch hard-coded colors and low-contrast text.
Common bugs: a hard-coded Colors.black text that vanishes on a dark surface, or a custom accent that never harmonized with the dynamic palette.
Quick Check: Choosing themeMode
You want your app to automatically match the user's wallpaper-based dynamic colors AND follow their OS light/dark setting, while still allowing an optional in-app override later. What is the right baseline configuration?
Recap: Adaptive Material 3 Theming
You built a palette that adapts to both wallpaper and brightness:
- Seed fallback:
ColorScheme.fromSeedfor light and dark when dynamic color is unavailable. - Dynamic color:
DynamicColorBuildersupplies wallpaper schemes, with?? seedSchemefallback. - Harmonization: blend brand and custom colors toward the dynamic primary.
- Semantic roles: read
colorSchemeroles instead of hard-coded hex so widgets flip automatically. - Mode control:
themeMode: ThemeMode.systemby default, with an optionalValueNotifiertoggle.
Adapt to the user, fall back gracefully, and never hard-code a color you could read from the scheme.
คำถามที่พบบ่อย
บทเรียน “สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Flutter Mobile Development ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Flutter Mobile Development มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท”
ปรับชุดสีให้เข้ากับวอลเปเปอร์ของผู้ใช้ และสลับระหว่างโหมดสว่างกับมืดได้อย่างราบรื่น คุณปฏิบัติ Flutter Mobile Development ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Flutter Mobile Development หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Flutter Mobile Development บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Flutter Mobile Development นี้ได้ไหม
ได้ บทเรียน Flutter Mobile Development ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ชุดสี Material 3 และสีตั้งต้น
- สีแบบไดนามิกและธีมสว่าง/มืดแบบปรับตามบริบท
- ThemeExtension แบบกำหนดเองสำหรับโทเค็นแบรนด์
- ตัวอักษรแบบตอบสนองและธีมขององค์ประกอบ