theme_provider 0.3.3

  • Readme
  • Changelog
  • Example
  • Installing
  • 95

theme_provider #

Easy to use, customizable Theme Provider. This provides app color schemes throughout the app and automatically rebuilds the UI dynamically. You can also persist your color theme as well. Easily store and retrieve user preference without hassle. This package also provides you with several widgets that can help you to easily add theme switching abilities. Additionally you can pass option classes to store and provide data which should be associated with the current theme.

Codemagic build status Pub Package

▶️ Basic Demonstration #

Web demo is available in https://kdsuneraavinash.github.io/theme_provider

Basic UsageDialog Box
RecordRecord

💻 Include in your project #

dependencies:
  theme_provider: <latest version>

run packages get and import it

import 'package:theme_provider/theme_provider.dart';

👨‍💻 Usage #

Basic Usage #

Wrap your material app like this to use dark theme and light theme out of the box.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      child: MaterialApp(
        home: ThemeConsumer(
          child: HomePage(),
        ),
      ),
    );
  }
}

Provide additional themes #

You may also provide additional themes using the themes parameter. Here you have to provide a theme id string and theme data value. (Make sure to provide unique theme ids)

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      themes: [
        AppTheme.light(), // This is standard light theme (id is default_light_theme)
        AppTheme.dark(), // This is standard dark theme (id is default_dark_theme)
        AppTheme(
          id: "custom_theme", // Id(or name) of the theme(Has to be unique)
          data: ThemeData(  // Real theme data
            primaryColor: Colors.black,
            accentColor: Colors.red,
          ),
        ),
      ],
      child: MaterialApp(
        home: ThemeConsumer(
          child: HomePage(),
        ),
      ),
    );
  }
}

Changing and accessing the current theme #

You can use the theme id strings to change the current theme of the app.

 ThemeProvider.controllerOf(context).nextTheme();
 // Or
 ThemeProvider.controllerOf(context).setTheme(THEME_ID);

Access current AppTheme

 ThemeProvider.themeOf(context)

Access theme data:

 ThemeProvider.themeOf(context).data
 // or
 Theme.of(context)

Apps with routing #

Wrap each route and dialog in ThemeConsumer.

MaterialPageRoute(
  builder: (_) => ThemeConsumer(child: SecondPage()),
),

Or you may wrap MaterialApp with ThemeConsumer. If you provide the theme consumer on MaterialTheme then you don't have to provide ThemeConsumer on routes. However that would disable the ability to use multiple theme controllers. Also a visible flickr may occur at the start of app when the saved theme is loaded.

This approach is much easier to integrate and works well with all other material components such as SearchDelegates and DialogBoxes without wrapping with ThemeConsumers.

Note that you have to pass the theme from ThemeProvider into the theme parameter of the MaterialApp if you try this approach.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      child: ThemeConsumer(
        child: Builder(
          builder: (themeContext) => MaterialApp(
            theme: ThemeProvider.themeOf(themeContext).data,
            title: 'Material App',
            home: MyApp(),
          ),
        ),
      ),
    );
  }

Provide callbacks for theme changing event #

If you want to change the StatusBarColor when the theme changes, you can provide a onThemeChanged callback to the ThemeProvider.

Passing Additional Options #

This can also be used to pass additional data associated with the theme. Use options to pass additional data that should be associated with the theme. eg: If font color on a specific button changes according to the current theme, create a class to encapsulate the value.

Options classes must implement or extend AppThemeOptions.

  class MyThemeOptions implements AppThemeOptions{
    final Color specificButtonColor;
    MyThemeOptions(this.specificButtonColor);
  }

Then provide the options with the theme.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      themes: [
        AppTheme(
          id: "light_theme",
          data: ThemeData.light(),
          options: MyThemeOptions(Colors.blue),
        ),
        AppTheme(
          id: "light_theme",
          data: ThemeData.dark(),
          options: MyThemeOptions(Colors.red),
        ),
      ],
      child: MaterialApp(
        home: ThemeConsumer(
          child: HomePage(),
        ),
      ),
    );
  }
}

Then the option can be retrieved as,

ThemeProvider.optionsOf<MyThemeOptions>(context).specificButtonColor

💾 Persisting theme #

Saving theme #

To persist themes, simply pass saveThemesOnChange as true. This will ensure that the theme is saved to the disk.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      saveThemesOnChange: true,
       child: MaterialApp(
        home: ThemeConsumer(
          child: HomePage(),
        ),
      ),
    );
  }
}

Or manually save the current theme by just using,

ThemeProvider.controllerOf(context).saveThemeToDisk();

Loading saved theme #

defaultThemeId will always be used to determine the initial theme. (If not provided the first theme you specify will be the default app theme.) But you can manually load the previous(saved) theme by using:

 ThemeProvider.controllerOf(context).loadThemeFromDisk();

To load a previously saved theme pass loadThemeOnInit as true:

ThemeProvider(
  child: MaterialApp(
    home: ThemeConsumer(
      child: HomePage(),
    ),
  ),
  saveThemesOnChange: true,
  loadThemeOnInit: true,
)

Or to load a theme/do some task at theme controller initialization use onInitCallback. This will get called on the start.

For example, snippet below will load the previously saved theme from the disk. (if previosuly saved.)

ThemeProvider(
  child: MaterialApp(
      home: ThemeConsumer(
        child: HomePage(),
      ),
    ),
  defaultThemeId: "theme_1",
  themes: [
    AppTheme.light(id: "theme_1"),
    AppTheme.light(id: "theme_2"),
    AppTheme.light(id: "theme_3"),
  ],
  saveThemesOnChange: true,
  onInitCallback: (controller, previouslySavedThemeFuture) async {
    // Do some other task here if you need to
    String savedTheme = await previouslySavedThemeFuture;
    if (savedTheme != null) {
      controller.setTheme(savedTheme);
    }
  },
)

🎁 Additional Widgets #

Theme Cycle Widget #

IconButton to be added to AppBar to cycle to next theme.

Scaffold(
  appBar: AppBar(
    title: Text("Example App"),
    actions: [CycleThemeIconButton()]
  ),
),

Theme Selecting Dialog #

SimpleDialog to let the user select the theme. Many elements in this dialog is customizable. Remember to wrap dialog is a ThemeConsumer.

showDialog(context: context, builder: (_) => ThemeConsumer(child: ThemeDialog()))

☑️ TODO #

  • [x] Add next theme command
  • [x] Add theme cycling widget
  • [x] Add theme selection by theme id
  • [x] Add theme select and preview widget
  • [x] Persist current selected theme
  • [x] Add unit tests and example
  • [x] Remove provider dependency
  • [x] Ids for theme_providers to allow multiple theme providers
  • [x] Add example to demostrate persistence

🐞 Bugs/Requests #

If you encounter any problems feel free to open an issue. Pull request are also welcome.

CHANGELOG #

[0.3.3] #

  • Added better error messages when description is not provided with theme. #1
  • Bumped shared_preferences version

[0.3.2] #

  • Used dependOnInheritedWidgetOfExactType instead of inheritFromWidgetOfExactType.
  • Modified README

[0.3.1] #

  • Added ability to add a onThemeChanged callback to ThemeProvider to be called after each time the theme is switched.

[0.3.0] #

  • Changed ThemeProvider to accept a child instead of builder.
  • Added ThemeConsumer widget.
  • Added loadThemeOnInit parameter.
  • Added providerId parameter to allow multiple theme providers.

[0.2.0+3] - 2019/06/25 #

  • Added onInitCallback parameter to ThemeProvider.
  • Modified README

[0.2.0+2] - 2019/06/11 #

  • Now theme can be saved manually using ThemeProvider.controllerOf(context).saveThemeToDisk()
  • Modified README

[0.2.0+1] - 2019/06/11 #

  • Fixed some typo and updated README and bumped example theme_provider version.

[0.2.0] - 2019/06/10 #

  • BREAKING CHANGE: loadThemesOnStartup is no longer supported. Use ThemeProvider.controllerOf(context).loadThemeFromDisk() manually to load theme.

[0.1.0+1] - 2019/06/09 #

  • Fixed some typos in code

[0.1.0] - 2019/06/09 #

  • BREAKING CHANGE: Removed context parameter from ThemeProvider builder.

[0.0.1+10] - 2019/06/09 #

  • Added ability to provide default theme id.
  • Added ability to persist current theme.

[0.0.1+9] - 2019/06/09 #

  • Changed ThemeProvider.themeOf(context) to return the current AppTheme.
  • Removed currentThemeId from ThemeCommand.

[0.0.1+8] - 2019/06/08 #

  • Added method to get all themes: ThemeProvider.controllerOf(context).allThemes
  • Made theme id required (instead of optional)
  • Added optional description field to theme
  • Added dialog to switch themes

[0.0.1+7] - 2019/06/08 #

  • Removed dependency on Provider.

[0.0.1+6] - 2019/06/08 #

  • Changed directory structure
  • Renamed,
    • ThemeCommand.of(context) to ThemeProvider.controllerOf(context)
    • AppThemeOptions.of(context) to ThemeProvider.optionsOf(context)
  • Added ThemeProvider.themeOf(context) to get theme.

[0.0.1+5] - 2019/06/08 #

  • Improved documentation

[0.0.1+4] - 2019/06/08 #

  • Added default values to themes. Now it is not required
  • Added AppTheme.light() and AppTheme.dark() to create dark and light themes easily
  • Added command, setTheme(id)
  • Improved tests

[0.0.1+3] - 2019/06/08 #

  • Fix a bug in the example

[0.0.1+2] - 2019/06/08 #

  • Changed api to pass on theme to help apply the theme to app

[0.0.1+1] - 2019/06/08 #

  • Fixed major bug relating to ProviderNotFoundError

[0.0.1] - 2019/06/08 #

  • Added base functionality to add custom theme data
  • Added functionality to cycle theme using the command

example/lib/main.dart

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      saveThemesOnChange: true,
      loadThemeOnInit: true,
      themes: <AppTheme>[
        AppTheme.light(),
        AppTheme.dark(),
        customAppTheme(),
      ],
      child: MaterialApp(
        home: ThemeConsumer(
          child: HomePage(),
        ),
      ),
    );
  }
}

AppTheme customAppTheme() {
  return AppTheme(
    id: "custom_theme",
    description: "Custom Color Scheme",
    data: ThemeData(
      accentColor: Colors.yellow,
      primaryColor: Colors.red,
      scaffoldBackgroundColor: Colors.yellow[200],
      buttonColor: Colors.amber,
      dialogBackgroundColor: Colors.yellow,
    ),
  );
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Example App")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text("Next Theme"),
              onPressed: ThemeProvider.controllerOf(context).nextTheme,
            ),
            RaisedButton(
              child: Text("Theme Dialog"),
              onPressed: () {
                showDialog(
                    context: context,
                    builder: (_) => ThemeConsumer(child: ThemeDialog()));
              },
            ),
            RaisedButton(
              child: Text("Second Screen"),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) => ThemeConsumer(child: SecondPage()),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
          child: Text("Next Theme"),
          onPressed: ThemeProvider.controllerOf(context).nextTheme,
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  theme_provider: ^0.3.3

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:theme_provider/theme_provider.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
89
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
95
Learn more about scoring.

We analyzed this package on Jul 8, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package does not support Flutter platform android

Because:

  • package:theme_provider/theme_provider.dart that imports:
  • package:theme_provider/src/widgets/theme_dialog.dart that imports:
  • package:theme_provider/src/provider/theme_provider.dart that imports:
  • package:theme_provider/src/provider/inherited_theme.dart that imports:
  • package:theme_provider/src/controller/theme_controller.dart that imports:
  • package:theme_provider/src/controller/shared_preferences_adapter.dart that imports:
  • package:shared_preferences/shared_preferences.dart that imports:
  • package:shared_preferences_linux/shared_preferences_linux.dart that declares support for platforms: linux

Package does not support Flutter platform ios

Because:

  • package:theme_provider/theme_provider.dart that imports:
  • package:theme_provider/src/widgets/theme_dialog.dart that imports:
  • package:theme_provider/src/provider/theme_provider.dart that imports:
  • package:theme_provider/src/provider/inherited_theme.dart that imports:
  • package:theme_provider/src/controller/theme_controller.dart that imports:
  • package:theme_provider/src/controller/shared_preferences_adapter.dart that imports:
  • package:shared_preferences/shared_preferences.dart that imports:
  • package:shared_preferences_linux/shared_preferences_linux.dart that declares support for platforms: linux

Package does not support Flutter platform macos

Because:

  • package:theme_provider/theme_provider.dart that imports:
  • package:theme_provider/src/widgets/theme_dialog.dart that imports:
  • package:theme_provider/src/provider/theme_provider.dart that imports:
  • package:theme_provider/src/provider/inherited_theme.dart that imports:
  • package:theme_provider/src/controller/theme_controller.dart that imports:
  • package:theme_provider/src/controller/shared_preferences_adapter.dart that imports:
  • package:shared_preferences/shared_preferences.dart that imports:
  • package:shared_preferences_linux/shared_preferences_linux.dart that declares support for platforms: linux

Package does not support Flutter platform web

Because:

  • package:theme_provider/theme_provider.dart that imports:
  • package:theme_provider/src/widgets/theme_dialog.dart that imports:
  • package:theme_provider/src/provider/theme_provider.dart that imports:
  • package:theme_provider/src/provider/inherited_theme.dart that imports:
  • package:theme_provider/src/controller/theme_controller.dart that imports:
  • package:theme_provider/src/controller/shared_preferences_adapter.dart that imports:
  • package:shared_preferences/shared_preferences.dart that imports:
  • package:shared_preferences_linux/shared_preferences_linux.dart that declares support for platforms: linux

Package does not support Flutter platform windows

Because:

  • package:theme_provider/theme_provider.dart that imports:
  • package:theme_provider/src/widgets/theme_dialog.dart that imports:
  • package:theme_provider/src/provider/theme_provider.dart that imports:
  • package:theme_provider/src/provider/inherited_theme.dart that imports:
  • package:theme_provider/src/controller/theme_controller.dart that imports:
  • package:theme_provider/src/controller/shared_preferences_adapter.dart that imports:
  • package:shared_preferences/shared_preferences.dart that declares support for platforms: android, ios, linux, macos, web

Package not compatible with SDK dart

Because:

  • theme_provider that is a package requiring null.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
shared_preferences ^0.5.6+1 0.5.8
Transitive dependencies
collection 1.14.12 1.14.13
file 5.2.1
flutter_web_plugins 0.0.0
intl 0.16.1
meta 1.1.8 1.2.1
path 1.7.0
path_provider_linux 0.0.1+2
path_provider_platform_interface 1.0.2
platform 2.2.1
plugin_platform_interface 1.0.2
process 3.0.13
shared_preferences_linux 0.0.2+1
shared_preferences_macos 0.0.1+10
shared_preferences_platform_interface 1.0.4
shared_preferences_web 0.1.2+7
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8
xdg_directories 0.1.0
Dev dependencies
flutter_test