Tutorial
Using Themes in Flutter
While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.
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.
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),
),
);
}
}
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.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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'),
);
}
}
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'),
);
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.