Table of contents
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.