A video restart exposes theme flashes that are easy to miss in isolated screenshots.
Supply theme darkTheme and themeMode
MaterialApp selects theme or darkTheme according to themeMode and platform brightness. Define both schemes instead of applying a dark background to a light component palette.
Omitting darkTheme makes ThemeMode.dark fall back to a generic dark theme rather than the brand design.
flutter pub add shared_preferences
Start with ThemeMode.system
ThemeMode.system follows the user device setting and updates when platform brightness changes. It is a respectful default until the user explicitly chooses an override.
Reading platformBrightness once at startup misses later system changes.
dependencies:
flutter:
sdk: flutter
shared_preferences: any
An Instagram dark-mode audit covers surfaces, artwork, charts, system chrome, and saved preference.
Derive matching schemes with brightness
Create light and dark ColorScheme.fromSeed calls with the same seed and different Brightness values. The generated tonal roles preserve identity while adapting surface and on-colour contrast.
Inverting raw colours manually rarely produces a complete accessible dark palette.

Build production-ready Dark Mode features
The Complete Flutter Guide turns dark mode into maintainable app architecture, polished UI, and testable production code.
Enrol nowShared preferences enters only at the persistence step, leaving ThemeMode switching independent of storage.
import 'package:shared_preferences/shared_preferences.dart';
Switch modes through ValueNotifier
A ValueNotifier
Creating the notifier inside build resets the selection on every rebuild.
final themeMode = ValueNotifier(ThemeMode.system);
ValueListenableBuilder<ThemeMode>(
valueListenable: themeMode,
builder: (_, mode, __) => MaterialApp(
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal)),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal, brightness: Brightness.dark),
),
themeMode: mode,
home: SettingsPage(onModeChanged: (value) => themeMode.value = value),
),
);
Persist the selected ThemeMode
Store mode.name with shared_preferences and parse it during startup, falling back to system for missing or unknown values. Write only after a user choice and keep the notifier value in sync with the successful preference update.
Storing an enum index is fragile when enum order changes.
Future<ThemeMode> loadThemeMode() async {
final prefs = await SharedPreferences.getInstance();
final saved = prefs.getString('themeMode');
return ThemeMode.values.where((mode) => mode.name == saved).firstOrNull
?? ThemeMode.system;
}
Future<void> saveThemeMode(ThemeMode mode) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('themeMode', mode.name);
}
The LinkedIn theming discussion separates platform brightness from user intent and effective appearance.

The Complete Flutter Guide: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowAdapt individual artwork to brightness
Theme.of(context).brightness can select assets, shadows, overlays, and system UI treatments that cannot come from ColorScheme alone. Keep branches small and semantic, using theme roles for ordinary colours.
Checking MediaQuery platform brightness ignores an explicit in-app override.
final brightness = Theme.of(context).brightness;
final logoAsset = brightness == Brightness.dark
? 'assets/logo-light.png'
: 'assets/logo-dark.png';
AnnotatedRegion<SystemUiOverlayStyle>(
value: brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark,
child: Image.asset(logoAsset),
)
Make brightness a complete product state
Separate the user’s preference from the effective platform brightness. A three-way choice—system, light, or dark—preserves intent when the operating system changes later. Persist that enum rather than saving only the currently resolved boolean. At startup, load it early enough to avoid a bright flash, or provide a neutral launch surface while preferences initialise.
Dark mode is not a mechanical inversion. Build a dedicated dark ColorScheme, inspect foreground contrast on every surface, and reduce large bright areas that cause glare. Images, illustrations, charts, code blocks, dividers, selection colours, and disabled states all need review. Elevation may be communicated with tonal separation or overlays rather than the same dark shadow used in the light design.
System chrome should follow the surface behind it. Update status-bar and navigation-bar icon brightness through an annotated region or app-bar configuration, then inspect Android and iOS devices because their system areas differ. Modal routes, splash screens, native pickers, and embedded web content can expose an unthemed flash even when ordinary Flutter routes are correct. Do not infer a person’s choice from time of day when the application already offers an explicit setting.
Test system mode while changing operating-system brightness, all three saved choices after process death, increased contrast, large text, and every brand illustration. A widget test can inject platform brightness and verify the effective theme without depending on the test machine. Analytics should record only the coarse preference if it is genuinely useful and disclosed. Animating a theme change can help preserve context, but respect reduced motion and ensure custom theme extensions interpolate. The switch itself must announce its current value and remain visible against both surfaces before the rest of the screen is considered finished.
Cached HTML, maps, charts, and remote images can defeat an otherwise complete dark theme. Pass an explicit appearance to embedded content where supported and provide a neutral surrounding surface while it loads. For data visualisation, choose series colours against both backgrounds and test overlapping lines, tooltips, and print or export output. Screenshots and shared images may need a fixed presentation independent of the viewer’s mode. State restoration should preserve the selected option, while “system” continues following live platform changes; these are separate behaviours and deserve separate tests.
Automated contrast tools are useful, but translucent layers and gradients still need checks in their composed state. Sample the rendered result and validate critical text by eye with assistive settings enabled.
Launch surfaces deserve special attention because they appear before the Flutter theme is available. Configure native splash colours for both brightness variants where the platform permits, and avoid a white asset background around a transparent dark-mode logo. If the saved preference cannot be read before startup, a neutral system-matched surface is less jarring than painting light mode and switching a moment later. Test a terminated launch, not only hot restart.
Pure black is not automatically the best dark surface. Slight tonal differences can express hierarchy, reduce halation around bright text, and keep cards distinct without heavy shadows. OLED power savings may matter for large areas, but readability and brand requirements still need evidence. Review screenshots, charts, maps, syntax highlighting, web views, and user-generated images at low display brightness. A contrast ratio measured from token values can miss translucent overlays, so sample the composed pixels for critical labels. Finally, confirm that scheduled notifications, exported images, and shared documents use an intentional appearance rather than inheriting an unavailable build context.
Desktop window chrome and browser theme-colour metadata should also follow the active surface where the host platform exposes them. Otherwise a finished dark route can still sit inside a bright outer frame. Verify these details after packaging, because development runners do not always match the installed application.
Common mistakes
- Supply theme darkTheme and themeMode: In Flutter dark mode, omitting darkTheme makes ThemeMode.dark fall back to a generic dark theme rather than the brand design; inspect this Flutter dark mode cause before changing another Flutter dark mode widget.
- Start with ThemeMode.system: In Flutter dark mode, reading platformBrightness once at startup misses later system changes; inspect this Flutter dark mode cause before changing another Flutter dark mode widget.
- Derive matching schemes with brightness: In Flutter dark mode, inverting raw colours manually rarely produces a complete accessible dark palette; inspect this Flutter dark mode cause before changing another Flutter dark mode widget.
- Switch modes through ValueNotifier: In Flutter dark mode, creating the notifier inside build resets the selection on every rebuild; inspect this Flutter dark mode cause before changing another Flutter dark mode widget.
- Persist the selected ThemeMode: In Flutter dark mode, storing an enum index is fragile when enum order changes; inspect this Flutter dark mode cause before changing another Flutter dark mode widget.
- Adapt individual artwork to brightness: In Flutter dark mode, checking MediaQuery platform brightness ignores an explicit in-app override; inspect this Flutter dark mode cause before changing another Flutter dark mode widget.
Frequently asked questions
How does supply theme darkTheme and themeMode work in Flutter dark mode?
For Flutter dark mode, the starting rule is that materialApp selects theme or darkTheme according to themeMode and platform brightness. Apply this Flutter dark mode rule first because supply theme darkTheme and themeMode determines whether the Flutter dark mode pattern fits.
Why does derive matching schemes with brightness matter for Flutter dark mode?
In Flutter dark mode, the generated tonal roles preserve identity while adapting surface and on-colour contrast. Keeping derive matching schemes with brightness at the Flutter dark mode call site exposes the Flutter dark mode return value directly.
What failure should I test first in Flutter dark mode?
First reproduce the Flutter dark mode case where storing an enum index is fragile when enum order changes. The persistence test saves a stable ThemeMode name, survives process death, and still follows platform brightness when the stored choice is system.
How can I verify Flutter dark mode before release?
Exercise adapt individual artwork to brightness with real Flutter dark mode inputs on every shipped platform. Inspect the final Flutter dark mode callback or output; a successful Flutter dark mode button tap alone is not proof.
Further reads
Connect Flutter dark mode to the surrounding Flutter stack through these published tutorials:
- Flutter Development Guide 2026
- Flutter ThemeData: App-Wide Colours and Typography
- Flutter Material 3: Dynamic Colour and New Components
- Flutter shared_preferences: Save Simple App Data
- Flutter Google Fonts and Custom Fonts Tutorial
Sources: Flutter framework and Dart API documentation; Flutter dark mode examples verified against current stable Flutter.