Flutter design: make your theme homogeneous

David Gonzalez
Flutter Community
Published in
5 min readJun 30, 2021

In a lot of Flutter source codes and applications, I observed a recurring practice consisting in adding custom style directly through widget parameters, leading to an inconsistent design and extra maintenance. As a personal example, I have to maintain a Flutter application where all titles had different font size (and sometime font weight).

In this article, I will explain you the importance of the way you should design your Flutter application, especially focusing on the theme.

In your opinion, what is the difference between these ‘settings’ pages in term of code ?

Settings page

What is the right way to apply a theme to your application ?

In the image above, ‘settings’ pages share the exact same code. There is strictly no difference between those four designs at this level.

There is nothing magic underneath: all theming related stuff have been centralized at higher level, in the MaterialApp widget. This widget allow you to define two themes, one for light brightness, the other for a dark theme mode.

Added to that, most of widgets are retrieving their design from it, if no value is given.

Widgets are setting their default values from the ThemeData (most of the time)

Let’s take an example on how to do this properly: the Card widget. You can observe that in the third example of settings page, the shape has ‘straight lines’ instead of corners.

whereas only ‘child’ attribute is used in code:
Card(child: …)

When you dig into how the Card widget has been designed, you an see how its shape is defined. Here is the code documentation about Card.shape attribute:

/// The shape of the card's [Material].
///
/// Defines the card's [Material.shape].
///
/// If this property is null then [CardTheme.shape] of [ThemeData.cardTheme]
/// is used. If that's null then the shape will be a [RoundedRectangleBorder]
/// with a circular corner radius of 4.0.
final ShapeBorder? shape;

So to ensure that ‘vanilla’ cards share the same design, you have to define your own ThemeData, and use it as theme (or darkTheme) in your MaterialApp widget.

ThemeData example() {
final base = ThemeData.dark();
final mainColor = Colors.lightBlue;
return base.copyWith(
cardColor: Color.lerp(mainColor, Colors.white, 0.2),
cardTheme: base.cardTheme?.copyWith(
color: Color.lerp(mainColor, Colors.black, 0.1),
margin: EdgeInsets.all(20.0),
elevation: 0.0,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(14.0),
side: BorderSide(color: Colors.white24, width: 1)),
),
);
}

The above code illustrate how to change the card design, but you can do it for pretty much every widgets coming from Flutter SDK. Also, you should centralize theme related stuff into a single file, because ThemeData is a huge data structure.

ThemeData covers every widgets, right ?

Unfortunately, ThemeData does not cover all widgets. For example, you cannot define list tiles design in there. Fortunately, you can achieve this through the ListTileTheme widget.

That how we can change list tile selected color and padding, without explicitly set it in the source code of the page.

Through ListTileTheme, we can redefine the elected tilebackground and foreground colors, without changing a single line of the page’s code.
ListView.builder(
itemBuilder: (context, index) {
final value = elements[index];
return ListTile(
selected: value == selected,
title: Text(value.title),
subtitle: Text(value.message),
leading: Icon(value.icon),
onTap: () => setState(() => selected = value),
);
},
itemCount: elements.length,
);

As you can see in the code, there is nothing related to the design. It as the advantage to avoid code noises, making it succinct and easier to understand. Also, one thing I like is in the fact that all my methods are small (less than 30 lines).

Conclusion

In this article, we saw how to centralize the application design into the ThemeData object. As you may understood, you may have to read a lot of the Flutter SDK code documentation for that purpose, but benefits will come when you or another colleague will have to maintain it:

  • Avoid code duplication
  • Less code in your pages, making code easier to be read and understood
  • Ensure design consistency

But as you may have seen, the ThemeData object is a really big structure, in constant evolution. So keep an eye on it !

Me looking at the ThemeData when starting a new project

To go further more…

Two more little tips for you! First, if you want to have a specific design for one of your component, instead of customizing it inside the build method, you can wrap your widget into the Theme widget.

When you design a new widget, you may want to imitate the SDK widgets, by retrieving theme information from the ThemeData. You may have seen that inheritate from ThemeData is not the right way. Instead, you can “extends” it through InheritedTheme widget, and imitate how it’s implemented like in ListTileTheme widget.

As always, you will be able to find source code and adobe design used for this article here:

Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm

--

--

David Gonzalez
Flutter Community

Hi, I’m David, a french mobile applications developer. As a freelance, I work on Android and iOS applications since 2010. I also work on Flutter now !