Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom theme not working properly. [Flutter]

Tags:

flutter

dart

I have created the following theme for my app:

ThemeData _buildDarkTheme() {
  final baseTheme = ThemeData(fontFamily: "Sunflower",);
  return baseTheme.copyWith(
      brightness: Brightness.dark,
      primaryColor: Colors.grey[800],
      accentColor: Colors.grey[850]);
}

I then apply it to my app as follows:

class MyApp extends StatelessWidget {
  MyApp({Key key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        theme: _buildDarkTheme(),
        home: new Scaffold(
          appBar: _buildAppBar(),
          body: new Container(
            color: Theme.of(context).accentColor,
            height: double.infinity,
            child: new ListView.builder(...

However, when I try to access the accent color inside the container (or anywhere else) instead of it being the expected, Colors.grey[850], it instead defaults to blue. Also, trying to use the custom font Sunflower font family does not work, but when I instead use

new Text("Hello World", style: new TextStyle(fontFamily: "Sunflower"))

The font appears correctly.

I am new to flutter and dart so any help resolving these issues would be appreciated.

like image 989
Adrian Avram Avatar asked Jun 18 '18 20:06

Adrian Avram


People also ask

How do you use custom themes on Flutter?

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. MaterialApp( title: appName, theme: ThemeData( // Define the default brightness and colors. brightness: Brightness.

How do you change the theme on Flutter?

In Flutter, we can change the theme throughout the app with the help of ThemeData in MaterialApp constructor. The default theme will be shared throughout the app when no theme is provided. ); ThemeData is used to configure the appearance of the entire app.


1 Answers

This is to do with how context and Theme.of work.

From the Theme class source code:

  static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
    final _InheritedTheme inheritedTheme =
        context.inheritFromWidgetOfExactType(_InheritedTheme);
    if (shadowThemeOnly) {
      if (inheritedTheme == null || inheritedTheme.theme.isMaterialAppTheme)
        return null;
      return inheritedTheme.theme.data;
    }

    final ThemeData colorTheme = (inheritedTheme != null) ? inheritedTheme.theme.data : _kFallbackTheme;
    final MaterialLocalizations localizations = MaterialLocalizations.of(context);
    final TextTheme geometryTheme = localizations?.localTextGeometry ?? MaterialTextGeometry.englishLike;
    return ThemeData.localize(colorTheme, geometryTheme);
  }

Theme.of (and Navigator.of(), ....of() etc), look at the context you pass them and then iterate upwards through the tree of widgets looking for a widget of the type specified.

Now, looking at your code

Widget build(BuildContext context) {
    return new MaterialApp(
        theme: _buildDarkTheme(),
        home: new Scaffold(
          appBar: _buildAppBar(),
          body: new Container(
            color: Theme.of(context).accentColor,

you can see that the context you're passing into Theme.of is actually the context above the theme you're creating. So it won't find your theme and will revert to the default. This is because the widget tree looks somewhat like the following (ignoring all the intermediate layers, with the arrow pointing to the context you're using.

MyApp - context <--------
  MaterialApp
    Theme
      Scaffold
        

There are two ways to fix this; the first is to use a Builder class to build your widget within a closure that has the context below the theme. That would look something like this:

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: _buildDarkTheme(),
      home: Scaffold(
        appBar: _buildAppBar(),
        body: Builder(
          builder: (context) => Container(
            color: Theme.of(context).accentColor,
            height: double.infinity,
            child: ListView.builder(...)
          ),
        ),
      ),
    );
  }
}

And it would make a tree that looks somewhat like this:

MyApp - context
  MaterialApp
    Theme
      Scaffold
        Builder - context <---------

The other (preferable) option is to split out the code for your builder into its own class - either a StatelessWidget-inherited class or a StatefulWidget and State pair.

like image 155
rmtmckenzie Avatar answered Oct 09 '22 03:10

rmtmckenzie