[FIXED] Flutter Dynamic Theming

Issue

What is the best way to go about dynamically changing the theme of a Flutter app? For example, if the user changes the color to red, I want the theme to instantly be changed to red. I can’t find anything very helpful online except one guy said to use the BLOC pattern, which I am not familiar with it. I’d like to hear your guys thoughts on the issue. Thanks!

My current code structure:

var themeData = ThemeData(
    fontFamily: 'Raleway',
    primaryColor: Colors.blue,
    brightness: Brightness.light,
    backgroundColor: Colors.white,
    accentColor: Colors.blue);

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Constants.appName,
      theme: themeData,
      home: CheckAuth(), //CheckAuth returns MyHomePage usually
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title, @required this.uid}) : super(key: key);

  final String title;
  final String uid;

  @override
  _MyHomePageState createState() => _MyHomePageState();
    }

class _MyHomePageState extends State<MyHomePage> {
    ...build and stuff
    }

Solution

You can use InhertedWidget if you like (instead of BLOC) – Basically it is used to access parent widget anywhere from the tree.

So what you should do is

  1. create InheritedWidget, somewhere in top of tree [from where you want the effect of theme to take place]
  2. wrap it around Theme widget
  3. expose a method to switch theme, by passing the ThemeData you want to replace it with.

Here is some code:

import 'package:flutter/material.dart';

var themeData = ThemeData(
    fontFamily: 'Raleway',
    primaryColor: Colors.blue,
    brightness: Brightness.light,
    backgroundColor: Colors.white,
    accentColor: Colors.blue
);

void main() {
  runApp(
    ThemeSwitcherWidget(
      initialTheme: themeData,
      child: MyApp(),
    ),
  );
}

class ThemeSwitcher extends InheritedWidget {
  final _ThemeSwitcherWidgetState data;

  const ThemeSwitcher({
    Key key,
    @required this.data,
    @required Widget child,
  })  : assert(child != null),
        super(key: key, child: child);

  static _ThemeSwitcherWidgetState of(BuildContext context) {
    return (context. dependOnInheritedWidgetOfExactType(ThemeSwitcher)
            as ThemeSwitcher)
        .data;
  }

  @override
  bool updateShouldNotify(ThemeSwitcher old) {
    return this != old;
  }
}

class ThemeSwitcherWidget extends StatefulWidget {
  final ThemeData initialTheme;
  final Widget child;

  ThemeSwitcherWidget({Key key, this.initialTheme, this.child})
      : assert(initialTheme != null),
        assert(child != null),
        super(key: key);

  @override
  _ThemeSwitcherWidgetState createState() => _ThemeSwitcherWidgetState();
}

class _ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
  ThemeData themeData;

  void switchTheme(ThemeData theme) {
    setState(() {
      themeData = theme;
    });
  }

  @override
  Widget build(BuildContext context) {
    themeData = themeData ?? widget.initialTheme;
    return ThemeSwitcher(
      data: this,
      child: widget.child,
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeSwitcher.of(context).themeData,
      home: CheckAuth(),
    );
  }
}

I have wrapped ThemeSwitcherWidget around MaterialApp so the effect is throughout the app (even when you push new route with Navigator).

Use ThemeSwitcher.of(context).switchTheme(themeData) anywhere below ThemeSwithcerWidget to change the theme.

In question’s case it should call ThemeSwitcher.of(context).switchTheme(Theme.of(context).copyWith(primaryColor: Colors.red)) to switch primary color to red throught out the app, for eg. on some button click

EDIT: replaced inheritFromWidgetOfExactType -> dependOnInheritedWidgetOfExactType, since it is deprecated – as pointed by Phoca in comments.

Answered By – Harsh Bhikadia

Answer Checked By – Robin (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published