adaptive_theme

v3.7.2

Allows to change between light and dark theme dynamically and add system adaptive theme support.

Package archive: https://pubdev.letsnova.ru/api/archives/adaptive_theme/3.7.2.tar.gz

Installdart pub add adaptive_theme

Readme

adaptive_theme

Adaptive Theme

The easiest way to add support for light and dark themes in your Flutter app. It allows you to manually set a light or dark theme and also lets you define themes based on the system. It also persists the theme mode changes across app restarts.

Build Tests Codecov Pub Version

Demo: Adaptive Theme

Index

Getting Started

Add the following dependency to your pubspec.yaml

dependencies:
                  adaptive_theme: <latest_version>
                

Initialization

You need to wrap your MaterialApp with AdaptiveTheme in order to apply themes.

import 'package:adaptive_theme/adaptive_theme.dart';
                import 'package:flutter/material.dart';
                
                void main() {
                  runApp(MyApp());
                }
                
                class MyApp extends StatelessWidget {
                
                  @override
                  Widget build(BuildContext context) {
                    return AdaptiveTheme(
                      light: ThemeData.light(useMaterial3: true),
                      dark: ThemeData.dark(useMaterial3: true),
                      initial: AdaptiveThemeMode.light,
                      builder: (theme, darkTheme) => MaterialApp(
                        title: 'Adaptive Theme Demo',
                        theme: theme,
                        darkTheme: darkTheme,
                        home: MyHomePage(),
                      ),
                    );
                  }
                }
                

Changing Theme Mode

Now that you have initialized your app as mentioned above. It's very easy and straightforward to change your theme modes: light to dark, dark to light, or to system default.

// sets theme mode to dark
                AdaptiveTheme.of(context).setDark();
                
                // sets theme mode to light
                AdaptiveTheme.of(context).setLight();
                
                // sets theme mode to system default
                AdaptiveTheme.of(context).setSystem();
                

Toggle Theme Mode

AdaptiveTheme allows you to toggle between light, dark, and system themes the easiest way possible.

AdaptiveTheme.of(context).toggleThemeMode();
                

Changing Themes

If you want to change the theme entirely, e.g., change all the colors to some other color swatch, then you can use setTheme method.

AdaptiveTheme.of(context).setTheme(
                  light: ThemeData(
                    useMaterial3: true,
                    brightness: Brightness.light,
                    colorSchemeSeed: Colors.purple,
                  ),
                  dark: ThemeData(
                    useMaterial3: true,
                    brightness: Brightness.dark,
                    colorSchemeSeed: Colors.purple,
                  ),
                );
                

Reset Theme

AdaptiveTheme is smart enough to keep your default themes handy that you provided at the time of initialization. You can fall back to those default themes in a very easy way.

AdaptiveTheme.of(context).reset();
                

This will reset your theme as well as theme mode to the initial values provided at the time of initialization.

Set Default Theme

AdaptiveTheme persists theme mode changes across app restarts and uses the default themes to set theme modes (light/dark) on. You can change this behavior if you want to set a different theme as the default theme other than the one provided at the time of initialization.

This comes in handy when you're fetching themes remotely on app starts and the setting theme as the current theme.

Doing so is quite easy. You would set a new theme normally as you do by calling setTheme method but this time, with a flag isDefault set to true.

This is only useful when you might want to reset to default theme at some point.

AdaptiveTheme.of(context).setTheme(
                  light: ThemeData(
                    useMaterial3: true,
                    brightness: Brightness.light,
                    colorSchemeSeed: Colors.blue,
                  ),
                  dark: ThemeData(
                    useMaterial3: true,
                    brightness: Brightness.dark,
                    colorSchemeSeed: Colors.blue,
                  ),
                  isDefault: true,
                );
                

Get ThemeMode at App Start

When you change your theme, the next app run won't be able to pick the most recent theme directly before rendering with the default theme for the first time. This is because at the time of initialization, we cannot run async code to get the previous theme mode. However, it can be avoided if you make your main() method async and load the previous theme mode asynchronously. The example below shows how it can be done.

void main() async {
                  WidgetsFlutterBinding.ensureInitialized();
                  final savedThemeMode = await AdaptiveTheme.getThemeMode();
                  runApp(MyApp(savedThemeMode: savedThemeMode));
                }
                
AdaptiveTheme(
                  light: lightTheme,
                  dark: darkTheme,
                  initial: savedThemeMode ?? AdaptiveThemeMode.light,
                  builder: (theme, darkTheme) => MaterialApp(
                    title: 'Adaptive Theme Demo',
                    theme: theme,
                    darkTheme: darkTheme,
                    home: MyHomePage(),
                  ),
                )
                

Notice that I passed the retrieved theme mode to my material app so that I can use it while initializing the default theme. This helps to avoid theme change flickering on app startup.

Listen to the theme mode changes

You can listen to the changes in the theme mode via a ValueNotifier. This can be useful when designing a theme settings screen or developing UI to show theme status.

AdaptiveTheme.of(context).modeChangeNotifier.addListener(() {
                  // do your thing.
                });
                

Or you can utilize it to react on UI with

ValueListenableBuilder(
                  valueListenable: AdaptiveTheme.of(context).modeChangeNotifier,
                  builder: (_, mode, child) {
                    // update your UI
                    return Container();
                  },
                );
                

Using a floating theme button overlay

Starting from v3.3.0, you can now set debugShowFloatingThemeButton to true and enable a floating button that can be used to toggle theme mode very easily. This is useful when you want to test your app with both light and dark themes without restarting the app or navigating to the settings screen where your theme settings are available.

AdaptiveTheme(
                  light: ThemeData.light(),
                  dark: ThemeData.dark(),
                  debugShowFloatingThemeButton: true, // <------ add this line
                  initial: AdaptiveThemeMode.light,
                  builder: (theme, darkTheme) => MaterialApp(
                    theme: theme,
                    darkTheme: darkTheme,
                    home: MyHomePage(),
                  ),
                );
                

Video

Caveats

Non-Persistent theme changes

This is only useful in scenarios where you load your themes dynamically from network in the splash screen or some initial screens of the app. Please note that AdaptiveTheme does not persist the themes, it only persists the theme modes(light/dark/system). Any changes made to the provided themes won't be persisted and you will have to do the same changes at the time of the initialization if you want them to apply every time app is opened. e.g changing the accent color.

Using SharedPreferences

This package uses shared_preferences plugin internally to persist theme mode changes. If your app uses shared_preferences which might be the case all the time, clearing your shared_preferences at the time of logging out or signing out might clear these preferences too. Be careful not to clear these preferences if you want it to be persisted.

/// Do not remove this key from preferences
                AdaptiveTheme.prefKey
                

You can use the above key to exclude it while clearing all the preferences.

Or you can call AdaptiveTheme.persist() method after clearing the preferences to make it persistable again as shown below.

final prefs = await SharedPreferences.getInstance();
                await prefs.clear();
                AdaptiveTheme.persist();
                

Using CupertinoTheme

Wrap your CupertinoApp with CupertinoAdaptiveTheme in order to apply themes.

import 'package:adaptive_theme/adaptive_theme.dart';
                import 'package:flutter/material.dart';
                
                void main() {
                  runApp(MyApp());
                }
                
                class MyApp extends StatelessWidget {
                
                  @override
                  Widget build(BuildContext context) {
                    return CupertinoAdaptiveTheme(
                      light: CupertinoThemeData(
                        brightness: Brightness.light,
                      ),
                      dark: CupertinoThemeData(
                        brightness: Brightness.dark,
                      ),
                      initial: AdaptiveThemeMode.light,
                      builder: (theme) => CupertinoApp(
                        title: 'Adaptive Theme Demo',
                        theme: theme,
                        darkTheme: darkTheme,
                        home: MyHomePage(),
                      ),
                    );
                  }
                }
                

Changing Cupertino Theme

// sets dark theme
                CupertinoAdaptiveTheme.of(context).setDark();
                
                // sets light theme
                CupertinoAdaptiveTheme.of(context).setLight();
                
                // sets system default theme
                CupertinoAdaptiveTheme.of(context).setSystem();
                

Contribution

You are most welcome to contribute to this project!

Please have a look at Contributing Guidelines, before contributing and proposing a change.

Liked Adaptive Theme?

Show some love and support by starring the repository.

Or You can

Buy Me A Coffee

License

Copyright © 2020 Birju Vachhani
                
                Licensed under the Apache License, Version 2.0 (the "License");
                you may not use this file except in compliance with the License.
                You may obtain a copy of the License at
                
                    http://www.apache.org/licenses/LICENSE-2.0
                
                Unless required by applicable law or agreed to in writing, software
                distributed under the License is distributed on an "AS IS" BASIS,
                WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                See the License for the specific language governing permissions and
                limitations under the License.
                

Changelog

3.7.2

  • Migrate Shared Preferences usage to new Async API.
  • Add AdaptiveTheme.read(context) method to read current theme without creating a dependency on it.

3.7.1+2

  • Re-generate example/web directory with latest Flutter version.
  • Bump up shared_preferences dependency's minimum constraints to 2.5.0.
  • Fix imports for Brightness.

3.7.1+1

  • Fix imports.

3.7.1

  • Code cleanup.
  • Remove deprecated class name usage from docs.

3.7.0

  • [BREAKING] Remove deprecated CupertinoAdaptiveThemeManager. Use AdaptiveThemeManager<CupertinoThemeData> instead.
  • [BREAKING] Require Flutter 3.27.0 or higher: Replace deprecated use of withOpacity with withValues.
  • Add overrideMode parameter to AdaptiveTheme and CupertinoAdaptiveTheme to override theme mode manually.
  • Fix unnecessary material imports.
  • Update missing docs for source code.

3.6.0

  • Migrate DebugFloatingThemeButton to Material 3.
  • Expose DebugFloatingThemeButton as a public widget for extensions to work with it.

3.5.0

  • Add support for dynamically changing debugShowFloatingThemeButton state using AdaptiveTheme.of(context).setDebugShowFloatingThemeButton(bool) method.
  • Allow reading state of debugShowFloatingThemeButton using AdaptiveTheme.of(context).debugShowFloatingThemeButton.

3.4.1

  • Fix readme example code.
  • Update example app for a simpler example code.
  • Update example to use Material 3.

3.4.0

  • FEAT: Add useSystem flag for toggleThemeMode method to toggle between light, dark only when the flag is set to false.
  • Add more tests.

3.3.1

  • Add pub topics to package metadata.
  • Upgrade dependencies.

3.3.0

  • Upgrade SDK constraints to Dart 3.0 and Flutter 3.10.0.
  • Refactor deprecated api usages to new ones.
  • Use WidgetsBinding.instance.platformDispatcher instead of PlatformDispatcher.instance since its recommended.

3.2.1

  • Fix missing inherited widget for CupertinoAdaptiveTheme.

3.2.0

  • Fix calling AdaptiveTheme.of or CupertinoAdaptiveTheme.of not creating a dependency on it.
  • Add screenshots for pub.dev.

3.1.1

  • Add fix_data.yaml for Flutter fix feature for deprecation quick fix suggestion.
  • Remove redundant code.
  • Update copyright headers.

3.1.0

  • CupertinoAdaptiveThemeManager is now deprecated and replaced with AdaptiveThemeManager<CupertinoThemeData> in favor of supporting theming for other UI frameworks. (e.g. Fluent UI). This will be removed in v4.0.0.
  • AdaptiveThemeManager is now generic typed where the generic type represents the type of the theme data object. Replace AdaptiveThemeManager with AdaptiveThemeManager<ThemeData>
  • AdaptiveThemeManager is now a mixin instead of an abstract class to reduce code duplication.

3.0.0

  • Upgrade to Flutter 3.
  • Update & fix tests.
  • Update AdaptiveThemeMode enum.
  • Fix lints warnings & refactor code.

2.3.1

  • Fixed Material theme not updating on system theme change.
  • Updated example android project.

2.3.0

  • Fixed Cupertino theme not changing when on system mode.
  • Internal code cleanup.
  • Removed isDefault option from setTheme method. Default are meant to come from AdaptiveTheme widget itself.
  • Added flutter lints.
  • Fixed doc comments and typos.
  • Added reset and custom theme options in the example app.
  • Fixed AdaptiveTheme's brightness and theme getters.
  • Fixed CupertinoAdaptiveTheme's brightness and theme getters.
  • Added Tests.

2.2.0

  • Added support for Cupertino theme.

2.1.1

  • Fixed #18
  • Dark theme not working properly on all platforms.

2.1.0

  • Fixed #16 - get theme and get darkTheme returns the same theme depended on mode
  • Added #15 - Notify listener when changing theme mode

2.0.0

  • Improved documentation
  • Stable null safety support
  • Calling AdaptiveTheme.of(context).toggleThemeMode() now will sequentially loop through AdaptiveThemeMode.light , AdaptiveThemeMode.dark and AdaptiveThemeMode.system instead of just AdaptiveThemeMode.light and AdaptiveThemeMode.dark.

2.0.0-nullsafety.1

  • Migrate to null safety.

1.1.0

  • Removed hard coded shared_preferences version.
  • Hide public constructors for ThemePreferences.
  • AdaptiveTheme.of() now returns instance of AdaptiveThemeManager instead of AdaptiveThemeState to set restrictions for accessing state directly.

1.0.0

  • Add option to get previous theme mode on app startup.

0.1.1

  • Add option to silently update theme without notifying. Useful when chaining multiple changes.

0.1.0

  • Supports theme modes: light, dart, system default.
  • Persists theme modes across app restarts.
  • Allows to toggle theme mode between light and dark.
  • Allows to set default theme.
  • Allows to reset to default theme.