• Stars
    star
    151
  • Rank 246,057 (Top 5 %)
  • Language
    Dart
  • License
    MIT License
  • Created over 5 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easy to use, customizable and pluggable Theme Provider.

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 Usage Dialog Box
Record Record

💻 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: ThemeConsumer(
        child: Builder(
          builder: (themeContext) => MaterialApp(
            theme: ThemeProvider.themeOf(themeContext).data,
            title: 'Material App',
            home: 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)
          description: "My Custom Theme", // Description of theme
          data: ThemeData(  // Real theme data
            primaryColor: Colors.black,
            accentColor: Colors.red,
          ),
        ),
      ],
      child: ThemeConsumer(
        child: Builder(
          builder: (themeContext) => MaterialApp(
            theme: ThemeProvider.themeOf(themeContext).data,
            title: 'Material App',
            home: 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

Wrapping material app with ThemeProvider

If you provide the theme consumer on MaterialApp 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.

Wrapping each route independently with ThemeProvider

However you could also wrap each route and dialog in ThemeConsumer instead of wrapping the whole material app. This will give a more granular control and will not cause a visual flikr. However, some integrations(eg: SearchDelegates) might not be trivial.

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

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",
          description: "Light Theme",
          data: ThemeData.light(),
          options: MyThemeOptions(Colors.blue),
        ),
        AppTheme(
          id: "light_theme",
          description: "Light Theme 2",
          data: ThemeData.dark(),
          options: MyThemeOptions(Colors.red),
        ),
      ],
      // ....
    );
  }
}

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,
      // ...
    );
  }
}

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(
  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(
  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);
    }
  },
  // ...
)

Dynamically Adding/Removing Themes

Themes can be dynamically added/removed via addTheme and removeTheme. Whether a theme id exists or not can be checked via hasTheme. A theme can only be added if it is not added previously. Similarly, a theme can only be removed if it is previously added. So the membership must be checked before adding/removing themes. (Whether a theme exists or not is decided via its theme id) Note that the active theme cannot be removed.

// Add theme
if (ThemeController.of(context).hasTheme('new_theme')){
  ThemeController.of(context).addTheme(newAppTheme);
}

// Remove theme
if (ThemeController.of(context).hasTheme('new_theme')){
  if (ThemeController.of(context).theme.id != 'new_theme'){
    ThemeController.of(context).removeTheme('new_theme')
  }
}

🔌 Checking system theme when loading initial theme

You can do this by simply checking for the system theme in onInitCallback callback. Following is an example usage. In the following snippet, the theme will be set to the previously saved theme. If there is no previously saved theme, it is set to light/dark depending on system theme.

You can use any kind of logic here to change the initialization callback.

Dynamically listening to theme changes is not yet available. The theme check is only possible on app start.

import 'package:flutter/scheduler.dart';


ThemeProvider(
  saveThemesOnChange: true, // Auto save any theme change we do
  loadThemeOnInit: false, // Do not load the saved theme(use onInitCallback callback)
  onInitCallback: (controller, previouslySavedThemeFuture) async {
    String savedTheme = await previouslySavedThemeFuture;

    if (savedTheme != null) {
      // If previous theme saved, use saved theme
      controller.setTheme(savedTheme);
    } else {
      // If previous theme not found, use platform default
      Brightness platformBrightness =
          SchedulerBinding.instance.window.platformBrightness;
      if (platformBrightness == Brightness.dark) {
        controller.setTheme('dark');
      } else {
        controller.setTheme('light');
      }
      // Forget the saved theme(which were saved just now by previous lines)
      controller.forgetSavedTheme();
    }
  },
  themes: <AppTheme>[
    AppTheme.light(id: 'light'),
    AppTheme.dark(id: 'dark'),
  ],
  child: ThemeConsumer(
    child: Builder(
      builder: (themeContext) => MaterialApp(
        theme: ThemeProvider.themeOf(themeContext).data,
        title: 'Material App',
        home: HomePage(),
      ),
    ),
  ),
);

🎁 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

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

🐞 Bugs/Requests

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

More Repositories

1

pulse-healthcare

Healthcare app and website for Semester Project
PHP
11
star
2

neomerce

Simple e-commerce web application
CSS
9
star
3

ballerina-shell

Simple jShell like REPL for Ballerina programming langiuage
Java
5
star
4

python-projects

Small python projects
Python
4
star
5

kvasir

Training machine learning model for KVASIR dataset
Jupyter Notebook
3
star
6

decrypt-site

Website for decrypt event 2019
HTML
3
star
7

syntax-api-quoter

This project aims to list the required Syntax API calls to create the given ballerina source code.
Ballerina
2
star
8

events

Combined repository of all events websites and apps
Dart
2
star
9

nanoprocessor

Nanoprocessor Project for 17-S2-CS2052 Computer Architecture
Verilog
2
star
10

CH-Bin

CH-Bin - metagenomic binning using convex-hull distance metric
Python
2
star
11

cse-night-app

App for CSE Night 2019: Night of the Pharaohs
Dart
2
star
12

dengue-app

Dart
1
star
13

robogames-2018

Walking Robot to IESL RoboGames 2018
Java
1
star
14

flutter-firebase-template

A sample template for Flutter + Firebase mobile apps
Dart
1
star
15

mental-healthcare-app

Mental Health Care App
Dart
1
star
16

events-flutter-redux

View and manage events
Dart
1
star
17

events-flutter-provider

Event app to subscribe and receive events from tags
Dart
1
star
18

winzig-compiler

Compiler that generates assembly code for the specified abstract machine for programs written in WinZig language
Java
1
star
19

xobots-2k19

XOBots 2019 - Tic Tac Toe playing robot
Java
1
star
20

project-medical

Medical search app
JavaScript
1
star
21

rpal-ast-interpreter

Rpal interpreter implemented in Java which evaluates AST trees
Java
1
star
22

asset-management-site

Asset Management Site Frontend
JavaScript
1
star
23

elmo

Trainer scripts for elmo model
Python
1
star
24

os-scheduler

OS Schedular Project
JavaScript
1
star
25

portfolio

Portfolio Website
TypeScript
1
star
26

machine-learning

Repo to log my progress on leaning machine learning.
Jupyter Notebook
1
star