native_theme_mode 0.3.0 copy "native_theme_mode: ^0.3.0" to clipboard
native_theme_mode: ^0.3.0 copied to clipboard

Sync Flutter ThemeMode to native Android night mode and iOS UI style so Android 12+ splash follows the in-app theme on the next cold start.

native_theme_mode #

pub package

native_theme_mode logo

Sync Flutter ThemeMode (light / dark / system) to the native platform so Android 12+ splash uses the in-app theme on the next cold start, and iOS overrideUserInterfaceStyle matches while the app is running.

Host apps only add the package and write Dart. This plugin contains all native code. You do not edit MainActivity.kt, AppDelegate.swift, AndroidManifest.xml, or Info.plist for this feature.

Demo #

Manual Light / Dark
native_theme_mode manual theme demo
System Mode
native_theme_mode system theme demo

The problem #

flutter_native_splash generates light and dark splash assets. It does not tell Android which one to use for an in-app theme.

Android 12+ draws the splash before Flutter starts. That splash follows the app’s persisted night mode from the previous session:

  • API 31+: UiModeManager.setApplicationNightMode(...)
  • API 30 and below: AppCompatDelegate.setDefaultNightMode(...)

There is no widely used Flutter plugin for this. Google’s Now in Android makes the same native call by hand.

iOS Launch Screen cannot follow an in-app theme the same way. The storyboard is shown by the system from device Dark Mode. This plugin still sets overrideUserInterfaceStyle so the running iOS UI matches.

This package does not replace flutter_native_splash, and it does not implement Flutter theming (ThemeData, Riverpod, Provider). Pair them: splash generates assets; this plugin chooses which Android splash to show on the next cold start.

Install #

flutter pub add native_theme_mode

Requires Flutter 3.10 or later (Dart 3.0).

Minimal usage #

import 'package:flutter/material.dart';
import 'package:native_theme_mode/native_theme_mode.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await NativeThemeMode.instance.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeMode>(
      valueListenable: NativeThemeMode.instance.themeModeListenable,
      builder: (context, mode, _) {
        return MaterialApp(
          themeMode: mode,
          theme: ThemeData.light(),
          darkTheme: ThemeData.dark(),
          home: const HomePage(),
        );
      },
    );
  }
}

// when the user toggles:
await NativeThemeMode.instance.setThemeMode(
  isDark ? ThemeMode.dark : ThemeMode.light,
);

Host apps may keep their own theme state and only call setThemeMode to sync native. MaterialApp.themeMode can also read NativeThemeMode.instance.themeMode directly.

Convenience methods: setDark(), setLight(), setSystem().

Pair with flutter_native_splash #

Generate light and dark splash assets, then let this plugin pick which Android uses:

dev_dependencies:
  flutter_native_splash: ^2.4.6

flutter_native_splash:
  color: "#FFFFFF"
  color_dark: "#121212"
  android_12:
    color: "#FFFFFF"
    color_dark: "#121212"
dart run flutter_native_splash:create

On iOS, color_dark still helps when the device is in Dark Mode. It does not make the Launch Screen follow an in-app-only dark theme.

Configuration #

configure is optional. Defaults already cover the simple case.

await NativeThemeMode.instance.configure(
  const NativeThemeModeConfig(
    storageKey: 'theme_mode',
    defaultMode: ThemeMode.system,
    persist: true,
    enableAndroid: true,
    enableIOS: true,
  ),
);
Option Default Notes
storageKey 'theme_mode' Key inside the plugin’s SharedPreferences / UserDefaults file (native_theme_mode). Not the host app’s prefs. Native code always opens the plugin file, so host apps never write native code. Changing this is advanced: Dart writes the custom name to the well-known meta key storage_key.
defaultMode ThemeMode.system Used if nothing was saved yet.
persist true If false, only apply for this process; the next Android splash may not match.
enableAndroid true Apply native night mode on Android.
enableIOS true Apply overrideUserInterfaceStyle on iOS.

Do not choose a custom SharedPreferences file name. Cold start cannot read a key that only Dart knows. The native file name is always native_theme_mode. Reserved key names: storage_key, default_mode, enable_android, enable_ios, persist.

Android behavior #

On setThemeMode:

  • Persist "light" / "dark" / "system" in the plugin prefs file.
  • API 31+: UiModeManager.setApplicationNightMode(MODE_NIGHT_NO / YES / AUTO).
  • Else: AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO / YES / FOLLOW_SYSTEM).

The splash of the current launch is decided by the OS from the previous setApplicationNightMode. To see a new splash:

  1. Toggle the theme in the app (setThemeMode).
  2. Fully kill the app (not just background it).
  3. Open it again.

Hot reload and hot restart do not change splash.

Host MainActivity stays FlutterActivity. No manifest edits are required. Flutter already lists uiMode in android:configChanges; setting night mode should not recreate the activity. The plugin adds androidx.appcompat:appcompat itself.

No custom Application class is required.

iOS #

iOS already follows the device Dark Mode setting by default. If you do not use this plugin on iOS (or you leave ThemeMode.system), launch screen and running UI still match the phone. You do not need this package for “follow the system” on iOS.

Use the iOS side when the in-app theme should stay light or dark even if the device is the opposite. Then the plugin sets window.overrideUserInterfaceStyle to .light, .dark, or .unspecified (system) and persists that in the plugin’s UserDefaults suite.

The Launch Screen / storyboard still uses system appearance. Plugin registration runs after the Launch Screen, so this package cannot change that splash for an in-app-only theme. That is an OS limit, not a missing feature.

iOS native code ships as both a Swift package and a CocoaPods podspec. Flutter 3.24+ apps can use Swift Package Manager; older apps keep using CocoaPods. Flutter will keep CocoaPods in maintenance mode until the CocoaPods registry becomes read-only (December 2, 2026).

Unsupported platforms #

Web and desktop have no native night-mode splash hook. Calls succeed as no-ops: Dart themeMode still updates so MaterialApp works, and nothing is written to a host prefs file.

FAQ #

Why is this launch’s splash still wrong?

The previous session had not called setThemeMode yet, or you did not fully kill the app after toggling. Android 12+ splash is chosen before Flutter starts, from the last persisted application night mode.

Does this replace flutter_native_splash?

No. That package generates assets. This package tells Android which night mode to use on the next cold start.

Can iOS Launch Screen follow my in-app theme?

No. iOS already follows the device Dark Mode setting by default. That includes the Launch Screen. This plugin only overrides the running UI when you pick an in-app light or dark theme.

API reference #

Public types (see dartdoc on each member):

License #

MIT. See LICENSE.

Source: github.com/RekanDev/native_theme_mode

0
likes
160
points
155
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Sync Flutter ThemeMode to native Android night mode and iOS UI style so Android 12+ splash follows the in-app theme on the next cold start.

Repository (GitHub)
View/report issues

Topics

#theme #splash #android #night-mode #theme-mode

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on native_theme_mode

Packages that implement native_theme_mode