Gravid Banner

Styling Flutter – Nightmare in Themesville

One of the great things about Flutter is how quickly you can progress with a project. Everyone says it. Flutter development is fast. Except when it isn’t. Where it comes unravelled for me is when I try and apply themes to my project. I’m a big fan of generic code. Declare something once, use it everywhere. On the face of it that is what themes are supposed to do. I can create an entry to decide the colour, or size of an icon by declaring it once in my theme and every time I use an icon it takes on these properties. Fantastic!

theme: ThemeData(
  iconTheme: (
    color: Colors.blue,
    size: 34),
),
Dart

This is great and it saves a lot of time messing with individual attributes of every icon and icon button being placed.

Unfortunately, not everything is so rosy. One of the first things that you discover, is that not all colours are created equal.

theme: ThemeData(
  primarySwatch: Colors.blue,
  iconTheme: (
    color: Color(0xff0066aa),
    size: 34),
),
Dart

The colour being declared in the two places are completely different. Colours.blue returns a material colour to the swatch which is not one colour, but a pantheon of colours. If I want to base my primary swatch off a custom colour I need to do a whole load of additional work – as outlined in this article..

Then there are the items which don’t actually have a colour attribute associated with them despite quite clearly having a colour. Like text buttons. After digging around in the depths of Flutter’s theme documentation we discover the following. In order to change the colour of a text button, we need to change the colour of the underlying text.

theme: ThemeData(
  textButtonTheme: TextButtonThemeData(
    style: TextButton.styleFrom(
      foregroundColor: Colors.blue,
      textStyle: TextStyle(
        fontSize: 16
      ),
    ),
  ),
),
Dart

And therein lies the problem. Flutters theme options are immense and often entirely non-obvious. Setting the primary swatch should, in theory, set the theme for your whole app – but sometimes it doesn’t and then you are left searching for where exactly the offending style is being set.

Take the following example

return AlertDialog(
  title: Text("Rename Item"),
  content: Column(
  children: [
    Text ("Item Name"),
    TextFormField(
      autofocus: true,
      maxLines: 1,
      initialValue: itemName,
      onChanged: (s) { itemName = s;} ),
    ],
  ),
);
Dart

The TextFormField has an underline. When I run my app the underline is purple. I have never declared anything purple anywhere. So where is it coming from? Following the conventions above, you might expect that there would be a ThemeData property called textFormFieldTheme with a method call of textFormFieldThemeData – but you would be wrong. Instead it is under the snappily named inputDecorationTheme, and the method breaks convention by having the same name.

inputDecorationTheme:   InputDecorationTheme(
),
Dart

But even here there are no colours – they are another two whole layers down -and there are a lot of options.

  • InputBorder? errorBorder,
  • InputBorder? focusedBorder,
  • InputBorder? focusedErrorBorder,
  • InputBorder? disabledBorder,
  • InputBorder? enabledBorder,
  • InputBorder? border,

In the end I found that it was the focussed border which was giving me the issue – but for good measure I declared the following against the day when I need two or more TextFormFields in a widget.

inputDecorationTheme:   InputDecorationTheme(
  enabledBorder: UnderlineInputBorder(
    borderSide: BorderSide (
      color: Colors.blue
    ),
  ),
  focusedBorder: UnderlineInputBorder(
    borderSide: BorderSide (
      color: Colors.blue
    ),
  ),
),
Dart


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *