Theming

Theming

Table of contents

No heading

No headings in the article.

When you receive a design from a UX designer, from for example Zeplin, everything is specified:

  • Colors
  • Fonts (Name, Size, Style)
  • Margins and padding of the elements

As in the Flutter counter-sample project, I started in my project with changing the primary color:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

But then I quickly realized that the primary color is a specific value in a range of colors, a ColorSwatch.

 static const MaterialColor blue = MaterialColor(
    _bluePrimaryValue,
    <int, Color>{
       50: Color(0xFFE3F2FD),
      100: Color(0xFFBBDEFB),
      200: Color(0xFF90CAF9),
      300: Color(0xFF64B5F6),
      400: Color(0xFF42A5F5),
      500: Color(_bluePrimaryValue),
      600: Color(0xFF1E88E5),
      700: Color(0xFF1976D2),
      800: Color(0xFF1565C0),
      900: Color(0xFF0D47A1),
    },
  );
  static const int _bluePrimaryValue = 0xFF2196F3;

Such a simple task, changing the primary color, was just not so simple anymore. How do I generate a range of colors from one specific color that was specified by the UX designer?

After some googling, I found the website: http://mcg.mbitson.com/. Here you can delegate the burden of creating a ColorSwatch. If you specify the primary color, this website will generate the color variants for you. Great!

I ended up copying the code of Colors.blue and changed it with the values from that website. Together with some other specific colors, I put that into a class:

class CompanyColor {
  static const MaterialColor primaryColor = MaterialColor(
    _primaryColor,
    <int, Color>{
      50: Color(0xffe1e4e8),
      100: Color(0xffb3bcc6),
      200: Color(0xff8090a0),
      300: Color(0xff4d647a),
      400: Color(0xff27425e),
      500: Color(_primaryColor),
      600: Color(0xff011d3b),
      700: Color(0xff011832),
      800: Color(0xff01142a),
      900: Color(0xff000b1c),
    },
  );

  static const int _primaryColor = 0xff012141;

  static const Color companyBlue = Color(0xff063a65);
  static const Color companyOrange = Color(0xfff98c1b);
  static const Color companyWhite = Color(0xffffffff);
  static const Color companyBlueLight = Color(0xff8fa1b7);
  static const Color companyBlueText = Color(0xff124370);
  static const Color companyRed = Color(0xffcd3631);
  static const Color companyGreen = Color(0xff008a57);
  static const Color companyYellow = Color(0xfffcc634);
  static const Color companyGrey = Color(0xffe4e8ed);
}

This was good. Now I had one place to put all the custom colors. But this was still not good enough. The UX designer has designed a consistent user interface, but I was repeating myself all over the place.

Referencing the colors by hand did not feel like applying the D.R.Y. principle. There had to be a better way. And, of course, there was: “Make a custom theme”. And the good news is that a theme is not only for colors but also for fonts, padding, etc.

Changing the theme is quite easy. Identify the object that you want to customize application-wide. Drill down to the Flutter source code.

For example for changing the cursor color:

  /// The color to use when painting the cursor.
  ///
  /// Defaults to [ThemeData.cursorColor] or [CupertinoTheme.primaryColor]
  /// depending on [ThemeData.platform].
  final Color cursorColor;

You can change that in a custom theme like this:

ThemeData companyTheme(BuildContext context) {
  var theme = Theme.of(context);
  return theme.copyWith(
    primaryColor: CompanyColor.primaryColor,
    iconTheme: theme.iconTheme.copyWith(
      color: CompanyColor.companyWhite,
    ),
    cursorColor: CompanyColor.companyOrange,
    inputDecorationTheme: theme.inputDecorationTheme.copyWith(
        contentPadding: EdgeInsets.only(bottom: 7.0, top: 4.0),
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: CompanyColor.underLineInputColor,
          ),
        ),
        focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: CompanyColor.underLineInputColor,
          ),
        ),
        focusedErrorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: CompanyColor.companyPink,
          ),
        ),
        errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: CompanyColor.companyPink,
          ),
        ),
        errorStyle: TextStyle(
          color: CompanyColor.companyRed,
          fontFamily: 'Lato-Bold',
          fontSize: 14.0,
        )),
  );
}

Apply this theme in your MaterialApp:

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: providers(isZebra: isZebra, scanWithCamera: scanWithCamera),
      child: Builder(
        builder: (BuildContext context) => MaterialApp(
          localizationsDelegates: [
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            GlobalCupertinoLocalizations.delegate,
          ],
          supportedLocales: [const Locale('nl')],
          title: 'Ferwerda BV',
          theme: companyTheme(context),
          navigatorObservers: [
            Provider.of<RouteObserver>(
              context,
              listen: false,
            ),
          ],
          home: S005StartupPage(),
        ),
      ),
    );
  }

That’s it. Now you have all theming in one place.