Flutter: How to change the MaterialApp theme at runtime

flutter dynamic theme
flutter theme generator
flutter custom theme color
flutter icon theme
flutter animated theme
flutter text theme
flutter theme font family
dynamic ui flutter

I have a MaterialApp Widget that sets the theme for all Widgets within the app. I'd like to change the MaterialApps theme value at runtime from a child Widget that doesn't have any direct reference to its parent MaterialApp.

It seems like this should be possible because the ThemeData is provided by an InheritedWidget, but I can't figure out how to change the theme wholesale. Does anyone know how to do this?

Here is the MaterialApp that owns the rest of the app:

new MaterialApp(
    title: 'App Name',
    theme: initialTheme,
    routes: <String, WidgetBuilder>{
      '/' : ...,
    },
),

Based on Dan Field's recommendation I came to the following solution. If anyone has improvements feel free to chime in:

// How to use: Any Widget in the app can access the ThemeChanger
// because it is an InheritedWidget. Then the Widget can call
// themeChanger.theme = [blah] to change the theme. The ThemeChanger
// then accesses AppThemeState by using the _themeGlobalKey, and
// the ThemeChanger switches out the old ThemeData for the new
// ThemeData in the AppThemeState (which causes a re-render).

final _themeGlobalKey = new GlobalKey(debugLabel: 'app_theme');

class AppTheme extends StatefulWidget {

  final child;

  AppTheme({
    this.child,
  }) : super(key: _themeGlobalKey);

  @override
  AppThemeState createState() => new AppThemeState();
}

class AppThemeState extends State<AppTheme> {

  ThemeData _theme = DEV_THEME;

  set theme(newTheme) {
    if (newTheme != _theme) {
      setState(() => _theme = newTheme);
    }
  }

  @override
  Widget build(BuildContext context) {
    return new ThemeChanger(
      appThemeKey: _themeGlobalKey,
      child: new Theme(
        data: _theme,
        child: widget.child,
      ),
    );
  }
}

class ThemeChanger extends InheritedWidget {

  static ThemeChanger of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ThemeChanger);
  }

  final ThemeData theme;
  final GlobalKey _appThemeKey;

  ThemeChanger({
    appThemeKey,
    this.theme,
    child
  }) : _appThemeKey = appThemeKey, super(child: child);

  set appTheme(AppThemeOption theme) {
    switch (theme) {
      case AppThemeOption.experimental:
        (_appThemeKey.currentState as AppThemeState)?.theme = EXPERIMENT_THEME;
        break;
      case AppThemeOption.dev:
        (_appThemeKey.currentState as AppThemeState)?.theme = DEV_THEME;
        break;
    }
  }

  @override
  bool updateShouldNotify(ThemeChanger oldWidget) {
    return oldWidget.theme == theme;
  }

}

How to dynamically change the theme in Flutter, Changing the theme at runtime and persisting those changes across restarts? You wrap your MaterialApp in the DynamicTheme widget. selectedTheme.distinct().map((theme) => theme.data); We use distinct() here to avoid repainting the same theme again. In order to use RxDart , we will need to add new package to our pubspec and

This is a specific case of the question answered here: Force Flutter to redraw all widgets

Take a look at the Stocks sample mentioned in that question, taking note especially of: https://github.com/flutter/flutter/blob/master/examples/stocks/lib/main.dart https://github.com/flutter/flutter/blob/master/examples/stocks/lib/stock_settings.dart

Take note of the following:

  1. Theme is specified from _configuration, which is updated by configurationUpdater
  2. configurationUpdater is passed on to children of the app that need it
  3. Children can call that configurationUpdater, which in turn sets state at the root of the app, which in turn redraws the app using the specified theme

Flutter: how to change the app's theme dynamically using streams, This function just returns the ThemeData class based on the last theme on stream and pass it to the theme property of the MaterialApp class. In its  If you want to change the theme of your app at compile time you can modify ThemeData. There is an attribute called Brightness which changes a bunch of colors from light to dark if set to Brightness.dark. We want it to dynamically change that variable during runtime. We therefore introduce state to the top level widget.

after various attempts I did it with the BLoC pattern, I don't know if it is a good method but it seems to work with no problems:

App theme models:

     class MyTheme {
      Brightness brightness;
      Color backgroundColor;
      Color scaffoldBackgroundColor;
      Color primaryColor;
      Brightness primaryColorBrightness;
      Color accentColor;

      MyTheme({
        this.brightness,
        this.backgroundColor,
        this.scaffoldBackgroundColor,
        this.primaryColor,
        this.primaryColorBrightness,
        this.accentColor
      });
    }

    class AppTheme {
      String name;
      MyTheme theme;
      AppTheme(this.name, this.theme);
    }

    List<AppTheme> myThemes = [
  AppTheme(
      'Default',
      MyTheme(
        brightness: Brightness.light,
        backgroundColor: Colors.blue[50],
        scaffoldBackgroundColor: Colors.blue[50],
        primaryColor: Colors.blue,
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.blue[50],
      )),
  AppTheme(
    'Teal',
    MyTheme(
      brightness: Brightness.light,
      backgroundColor: Colors.teal[50],
      scaffoldBackgroundColor: Colors.teal[50],
      primaryColor: Colors.teal[600],
      primaryColorBrightness: Brightness.dark,
      accentColor: Colors.teal[50],
    ),
  ),
];

App BLoC class. Here I used a BehaviorSubject of RxDart.

    class AppBloc {

  final _theme = BehaviorSubject<AppTheme>();
  Function(AppTheme) get inTheme => _theme.sink.add;
  Stream<AppTheme> get outTheme => _theme.stream;


  AppBloc() {
    print('-------APP BLOC INIT--------');

    // Send to stream the initial theme    
    inTheme(myThemes[0]);
  }

  dispose() {
    print('---------APP BLOC DISPOSE-----------');
    _theme.close();
  }
}

In the settings page of the app I use the _theme stream to set the current theme of a dropdown menu with the themes list. With the onChanged handler, when a user clicks on the theme it is sent to stream:

StreamBuilder(
                    stream: widget.bloc.outTheme,
                    builder: (context, AsyncSnapshot<AppTheme> snapshot) {
                      return snapshot.hasData
                          ? DropdownButton<AppTheme>(
                              hint: Text("Status"),
                              value: snapshot.data,
                              items: myThemes.map((AppTheme appTheme) {
                                return DropdownMenuItem<AppTheme>(
                                  value: appTheme,
                                  child: Text(appTheme.name),
                                );
                              }).toList(),
                              onChanged: widget.bloc.inTheme,
                            )
                          : Container();
                    }),

And finally in the homepage, with a StreamBuilder I use the _theme stream to set the selected ThemeData:

StreamBuilder(
          stream: _bloc.outTheme,
          builder: (context, AsyncSnapshot<AppTheme> snapshot) {
            return MaterialApp(
                theme: snapshot.hasData ? _buildThemeData(snapshot.data) : ThemeData(),
                home: HomePage());
          }),

_BuildThemeData method to get the ThemeData from the theme model:

_buildThemeData(AppTheme appTheme) {    
return ThemeData(
  brightness: appTheme.theme.brightness,
  backgroundColor: appTheme.theme.backgroundColor,
  scaffoldBackgroundColor: appTheme.theme.scaffoldBackgroundColor,
  primaryColor: appTheme.theme.primaryColor,
  primaryColorBrightness: appTheme.theme.primaryColorBrightness,
  accentColor: appTheme.theme.accentColor
);

}

I hope this is useful to you.

How to change the app's theme at runtime using the BLoC pattern in , We are going to build a Flutter application that consists of: A MaterialApp at the root of our app; Two pages: the HomePage and the  In this video, I will explain how to change themes at runtime. I will show you two approaches for the same. Please give stars for this project on git and like the video. Source Code - https

Flutter: Dynamic Theming, Changes the theme during runtime, also presists brightness settings theme) { return new MaterialApp( title: 'Flutter Demo', theme: theme,  Flutter’s Material widgets also use your Theme to set the background colors and font styles for AppBars, Buttons, Checkboxes, and more. Creating an app theme. To share a Theme across an entire app, provide a ThemeData to the MaterialApp constructor. If no theme is provided, Flutter creates a default theme for you.

Flutter: Dynamic Dark/Light Theme with Provider, In fact, app-wide themes are just Theme widgets created at the root of an app by the MaterialApp . After defining a Theme, use it within your own widgets. Flutter's​  Material Design has a notion of adapting to the target platform. That means it's conceptually fine for material.dart to take a dependency on cupertino.dart and for MaterialPageRoute to know about both _MountainViewPageTransition and _CupertinoPageTransition.

dynamic_theme, Changing the theme in runtime and persisting it was always a headache… a main and a stateless widget named App that returns MaterialApp, and of course a​  The MaterialApp configures the top-level Navigator to search for routes in the following order: For the / route, the home property, if non-null, is used. Otherwise, the routes table is used, if it has an entry for the route.

Comments
  • how to use this class in other pages?
  • could you provide more details on how to use this?