Making mobile apps look better - theme and colours

Build and publish a Flutter-based app for the App Store and Google Play — updated for 2026, where Material Design 3 (also called Material You) is now the default and the tooling has moved on significantly.

2014 Baseline Material color theme 

Material Design 2 was the standard and a 12-colour baseline theme was the starting point for every Flutter ap. This 2014 Baseline Material color theme made its debut with a built-in, baseline theme with 12 colours named and defined. Much has changed but these building blocks are important to understand as you think about the colours in your app.

Primary
#6200EE
Primary Variant
#3700B3
Secondary
#03DAC6
Secondary Variant
#018786

 

Background
#FFFFFF
Surface
#FFFFFF
Error
#B00020

 

On Primary
#FFFFFF
On Secondary
#000000

 

On Background
#000000
On Surface
#000000
On Error
#FFFFFF

 

Material Design 3 and Material Theme Builder

That world has changed substantially. Material Design 3 is now Flutter's default theming system, and the colour model has expanded from 12 named roles to over 30, built around a dynamic, tonal palette system. The old primarySwatch and MaterialColor approach is now deprecated in favour of ColorScheme and ThemeData.colorScheme.

The tooling has changed too. The original Material Design Colour Tool at material.io has been retired. Its replacement is the Material Theme Builder, available at material-foundation.github.io/material-theme-builder/. This tool generates a complete Material 3 colour scheme — including light and dark variants — from a single source colour, and can export directly to Flutter Dart code. It is the tool you should be using today.

This article has been revised throughout to reflect these changes. The core ideas — picking colours that complement your logo, understanding colour roles, and supporting both light and dark appearance — remain just as relevant. The implementation is simply more powerful now.

TL:DR – The colours in a mobile app can look polished if you pay attention to themes and colour choices. Modern tools make it easier than ever to generate a full Material 3 colour scheme from your brand colour, and this article shows how.

Making apps look good with colour

The image, app name, and AppBar title created in the previous part of this series don't look particularly refined. Adding a coherent colour palette gives the app depth and a sense of intentional design. Material Design 3 makes this systematic — and the tooling in 2026 makes it faster than it has ever been.

The Material Design 3 colour system helps you create a coherent, accessible set of colours for your app, with built-in support for dark and light themes. Rather than picking individual hex values and wiring them up manually, the system encourages you to define a small number of key colours and let the tooling derive the full palette.

Many apps still run on defaults and look generic as a result. Taking even a short time to apply a brand-derived colour scheme makes an app feel considered and professional.

The Material Design 3 colour model

Material Design 3 replaces the old 12-role baseline with a richer set of colour roles organised around five key colour groups: Primary, Secondary, Tertiary, Error, and Neutral. Each group generates a tonal palette — a range of shades from light to dark — and specific roles are drawn from those palettes for use on surfaces, containers, and content.

The key roles you will encounter most often in Flutter are shown below. Each has a corresponding "on" colour for text and icons placed on top of it.

Primary
#892807
On Primary Container
(light tonal)
Secondary
#EC7C55
On Secondary Container
(light tonal)

 

Surface
#FFFFFF
Surface Variant
(warm tint)
Error
#B3261E

 

In Flutter, all of these roles are managed through a single ColorScheme object rather than individual named properties scattered across ThemeData. This is a significant improvement over the Material 2 approach.

Choosing colours

There is one defining colour in this app: the logo colour #e5440d. The logo needs to be visible in the app and must not clash with the surrounding UI. Logos are often a source of friction in client projects — everyone has an opinion — so one practical approach is to derive complementary colours directly from the logo and apply them consistently. That way the conversation stays focused on the app rather than relitigating brand decisions.

Taking shades and tints of the logo colour as the basis for the colour palette keeps things harmonious. A shade (darker version) works well as the primary colour, while a tint (lighter version) suits the secondary role.

A good starting point for exploring shades and tints is color-hex.com.

The primary colour chosen is #892807 — a shade of the logo colour #e5440d. The secondary colour is #ec7c55 — a tint of the same logo colour. These two values are the inputs you need for the Material Theme Builder.

Using the Material Theme Builder

The Material Theme Builder is the current, actively maintained tool from the Material Design team for generating colour schemes. It replaces the old Material Design Colour Tool that was previously linked from material.io and is no longer available.

The workflow is straightforward:

  • Open the Material Theme Builder.
  • Enter your primary colour — in this case #892807.
  • Optionally set a secondary colour: #ec7c55.
  • The tool generates a full tonal palette and previews it across a range of Material 3 UI components in both light and dark variants.
  • Use the Export option to download the Flutter/Dart code directly — this gives you a ready-to-use ColorScheme and ThemeData configuration.

The exported code is far more complete than what the old tool produced. It handles light and dark schemes simultaneously, and every colour role is accounted for. This is worth the few minutes it takes.

Tonal palettes in Material Design 3

Behind the scenes, Material Design 3 generates tonal palettes — sequences of shades numbered from 0 (black) to 100 (white) — for each key colour. Specific roles in the colour scheme are drawn from specific tonal values. For example, the primary role in light mode might use tone 40, while primaryContainer uses tone 90.

You do not need to manage these tonal values manually. The Material Theme Builder handles this. What matters is understanding that the system is designed so that any colour you choose will produce an accessible, harmonious scheme — contrast ratios are baked in by design.

Material Design tonal colour palettes

Adding the colour scheme to your Flutter project

The Material Theme Builder exports a Dart file containing your ColorScheme definitions. The structure looks like this — the exact values will reflect your chosen colours:

ColorScheme from Material Theme Builder export
Add the exported color_schemes.g.dart to your project
import 'package:flutter/material.dart';

const lightColorScheme = ColorScheme(
  brightness: Brightness.light,
  primary: Color(0xFF892807),
  onPrimary: Color(0xFFFFFFFF),
  primaryContainer: Color(0xFFFFDBD1),
  onPrimaryContainer: Color(0xFF3A0800),
  secondary: Color(0xFFEC7C55),
  onSecondary: Color(0xFFFFFFFF),
  secondaryContainer: Color(0xFFFFDBCF),
  onSecondaryContainer: Color(0xFF3E1505),
  tertiary: Color(0xFF6D5E00),
  onTertiary: Color(0xFFFFFFFF),
  tertiaryContainer: Color(0xFFFFE168),
  onTertiaryContainer: Color(0xFF211B00),
  error: Color(0xFFB3261E),
  onError: Color(0xFFFFFFFF),
  errorContainer: Color(0xFFF9DEDC),
  onErrorContainer: Color(0xFF410E0B),
  background: Color(0xFFFFFBFF),
  onBackground: Color(0xFF201A19),
  surface: Color(0xFFFFFBFF),
  onSurface: Color(0xFF201A19),
  surfaceVariant: Color(0xFFF5DED9),
  onSurfaceVariant: Color(0xFF534340),
  outline: Color(0xFF857370),
  shadow: Color(0xFF000000),
  inverseSurface: Color(0xFF362F2E),
  onInverseSurface: Color(0xFFFBEEEC),
  inversePrimary: Color(0xFFFFB4A4),
);

const darkColorScheme = ColorScheme(
  brightness: Brightness.dark,
  primary: Color(0xFFFFB4A4),
  onPrimary: Color(0xFF5C1300),
  primaryContainer: Color(0xFF7C1E00),
  onPrimaryContainer: Color(0xFFFFDBD1),
  secondary: Color(0xFFFFB59C),
  onSecondary: Color(0xFF5C2210),
  secondaryContainer: Color(0xFF7A3826),
  onSecondaryContainer: Color(0xFFFFDBCF),
  tertiary: Color(0xFFE2C542),
  onTertiary: Color(0xFF393000),
  tertiaryContainer: Color(0xFF524600),
  onTertiaryContainer: Color(0xFFFFE168),
  error: Color(0xFFF2B8B5),
  onError: Color(0xFF601410),
  errorContainer: Color(0xFF8C1D18),
  onErrorContainer: Color(0xFFF9DEDC),
  background: Color(0xFF201A19),
  onBackground: Color(0xFFEDE0DE),
  surface: Color(0xFF201A19),
  onSurface: Color(0xFFEDE0DE),
  surfaceVariant: Color(0xFF534340),
  onSurfaceVariant: Color(0xFFD8C2BE),
  outline: Color(0xFFA08C89),
  shadow: Color(0xFF000000),
  inverseSurface: Color(0xFFEDE0DE),
  onInverseSurface: Color(0xFF362F2E),
  inversePrimary: Color(0xFF892807),
);

 

Applying light and dark themes

With the exported colour schemes in place, wiring them into your app's ThemeData is clean and explicit. The old primarySwatch property is deprecated in current Flutter — use colorScheme instead:

Light and dark theme configuration
Update main.dart to use the generated colour schemes
import 'color_schemes.g.dart';

MaterialApp(
  theme: ThemeData(
    useMaterial3: true,
    colorScheme: lightColorScheme,
  ),
  darkTheme: ThemeData(
    useMaterial3: true,
    colorScheme: darkColorScheme,
  ),
  themeMode: ThemeMode.system,
  // ...
)

 

Setting useMaterial3: true ensures your app uses the current design system. Setting themeMode: ThemeMode.system means the app automatically follows the device's light or dark appearance preference — no extra logic needed.

Repositioning the logo image
Move the logo to the App Bar using the leading property

The logo looks out of place in the body of the screen. Moving it to the leading position in the AppBar is more conventional. App Bar icons should be 24px high. Create a white version of the logo image for use on the coloured App Bar background — you will likely need a dark version later too.

appBar: AppBar(
  title: const Text('Your Project'),
  leading: Image.asset('assets/ezone-24-white.png'),
),

 

The app now has a theme that responds correctly to both light and dark appearance. The logo sits properly in the App Bar. However, the body text — currently reading Launch screen — is not adapting to the dark theme and becomes invisible against the dark background.

Flutter app light appearance on iOS   Flutter app dark appearance on iOS

Light and dark appearance on iOS. The words Launch screen are invisible in dark mode — the next article addresses this.

"On" colours and content legibility

Every colour role in Material Design 3 has a corresponding "on" colour — the colour intended for text, icons, and strokes placed on top of that surface. For example, text on a primary background should use onPrimary, and text on a surface should use onSurface.

In practice, if you use Material 3 components correctly — Text, ElevatedButton, Card, and so on — Flutter applies these "on" colours automatically from your ColorScheme. Where you are setting colours manually, always reach for the scheme's "on" value rather than hardcoding black or white. This is what makes the colour system genuinely useful for dark mode support.

The missing text in the dark appearance screenshot above is a case where a hardcoded or default colour is being used instead of the appropriate scheme colour. The next article in this series fixes that and covers content theming more thoroughly.