Using Themes in Flutter

Joshua Hall

One great aspects of Flutter is its use of UI packages like the Material and Cupertino design systems. They work to shortcut all of the minute and repetitive design choices like the appBar height or the shadows on buttons. But obviously always using the same default design patterns would quickly lead to a lot of very boring looking apps. We’re going to explore some of the methods for changing the overall look across our apps with Flutter themes.

Boilerplate

For the sake of simplicity, we’re just going to the use a demo counter app and focus on the Material design library, since they have everything we need to get started.

main.dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Demo Counter App

Default Themes

Google’s Material package comes with two baked-in themes, a light version (which is the default) and a dark version, if that’s more your style.

To set our styles across our entire app we need to set the theme to a method on ThemeData in the MaterialApp widget, in this case either the light() or dark() options.

main.dart

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

Flutter Dark Demo

That’s cool and all, but it’s still extremely limiting. Let’s jazz it up a bit.

Global Themes

Instead of using a method on ThemeData we can pass what we want to change directly into it. There’s a long list of what we can alter like the primaryColor, fontFamily, and even the cursorColor. You can explore the full list here, which may seem a bit overwhelming, but you really can do a lot with just a few of them.

A few of the properties on ThemeData also have a brightness counterpart, these just control the widgets on top of them. So accentColor would change the button but accentColorBrightness will change the text or icon on the button. We need to use either the light or dark properties on Brightness to do that.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primaryColor: Colors.purple[800],
        accentColor: Colors.amber,
        accentColorBrightness: Brightness.dark
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

Global Themes Example

Adapting Themes

That’s cool and all, but what if we like most parts of a theme and want to just change a few things without having to rewrite the whole thing? To extend a theme, we can use the copyWith method to extend it and pass in our custom styles.

return MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData.dark().copyWith(
    primaryColor: Colors.purple[800],
    accentColor: Colors.amber,
  ),
  home: MyHomePage(title: 'Flutter Demo Home Page'),
);

Adapting Themes Example

Using Themes

Right now, altering our theme will only influence our UI when we’re using the Material widgets. When we create something like a scaffold, it’s using ThemeData as the default for all of its styles until we explicitly pass something in. If we wanted to create a new, unique, widget it wouldn’t know to use something like primaryColor as its background color. We can use Theme.of() to access all of the properties on ThemeData.

floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  tooltip: 'Increment',
  backgroundColor: Theme.of(context).accentColor,
  child: Icon(Icons.add),
),

Conclusion

To some, Flutter themes may still seem very limiting when compared to the flexibility of CSS, but they are still one of the fundamental aspects of creating a consistent-looking app and keeping your codebase DRY.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#