Colorful umbrellas

Flutter: how to create a better MaterialColor from Color

Written by

in

In Flutter, MaterialColor is a class that defines a color palette to be used within a Material Design app. It creates a set of shades for a given primary color, which can be used in various components of the UI, such as buttons, text fields, and navigation bars. MaterialColor objects can be created from a single color value and then generate shades based on a specified brightness factor. This allows for a consistent and harmonious color scheme throughout the app.

To create a MaterialColor from a Color, you can use the MaterialColor class in Dart. The MaterialColor constructor takes two arguments:

  • The value of the color in RGB.
  • A map of shades of the color with keys representing the shade level and values representing the Color of that shade.

Let’s say we want to create a MaterialColor from the RGB color 176 / 255 / 38:

color palette

To use an implementation, let’s create a static method in a MaterialColorGenerator class and call:

Color color = Color.fromARGB(255, 176, 255, 38);
MaterialColor material = MaterialColorGenerator.from(color);

MaterialColor from opacity only

Now there are different ways to define the shades. Most tutorials propose to change the opacity for each shade, which will result in such a code:

import 'package:flutter/material.dart';

class MaterialColorGenerator{
  static MaterialColor from(Color color) {
    return MaterialColor(color.value, <int, Color>{
      50: color.withOpacity(0.1),
      100: color.withOpacity(0.2),
      200: color.withOpacity(0.3),
      300: color.withOpacity(0.4),
      400: color.withOpacity(0.5),
      500: color.withOpacity(0.6),
      600: color.withOpacity(0.7),
      700: color.withOpacity(0.8),
      800: color.withOpacity(0.9),
      900: color.withOpacity(1.0),
    });
  }
}

Let’s take our color example and look at the result:

color palette
Color values from shade50 (top) to shade900 (bottom).

This is a good result for lighter colors, but results in very similar colors for the “darker” shades.

MaterialColor from color variations only

To create some real color variations, it’s better to adjust the color values itself by using:

import 'package:flutter/material.dart';

class MaterialColorGenerator{
  static MaterialColor from(Color color) {
    List strengths = <double>[.05];
    final swatch = <int, Color>{};
    final int r = color.red, g = color.green, b = color.blue;

    for (int i = 1; i < 10; i++) {
      strengths.add(0.1 * i);
    }
    for (var strength in strengths) {
      final double ds = 0.5 - strength;
      swatch[(strength * 1000).round()] = Color.fromRGBO(
        r + ((ds < 0 ? r : (255 - r)) * ds).round(),
        g + ((ds < 0 ? g : (255 - g)) * ds).round(),
        b + ((ds < 0 ? b : (255 - b)) * ds).round(),
        1,
      );
    }
    return MaterialColor(color.value, swatch);
  }
}

Let’s take our color example and look at the result:

color palette
Color values from shade50 (top) to shade900 (bottom).

Now the result is good for “darker” shades (> 500), but there is less variation for the “lighter” colors.

A better MaterialColor from opacity and color variations

Let’s find a version that combines both of the advantages:

import 'dart:math';
import 'package:flutter/material.dart';

class MaterialColorGenerator{
  static MaterialColor from(Color color) {
    return MaterialColor(color.value, {
      50: tintColor(color, 0.9),
      100: tintColor(color, 0.8),
      200: tintColor(color, 0.6),
      300: tintColor(color, 0.4),
      400: tintColor(color, 0.2),
      500: color,
      600: shadeColor(color, 0.1),
      700: shadeColor(color, 0.2),
      800: shadeColor(color, 0.3),
      900: shadeColor(color, 0.4),
    });
  }

  static int tintValue(int value, double factor) =>
      max(0, min((value + ((255 - value) * factor)).round(), 255));

  static Color tintColor(Color color, double factor) => Color.fromRGBO(
      tintValue(color.red, factor),
      tintValue(color.green, factor),
      tintValue(color.blue, factor),
      1);

  static int shadeValue(int value, double factor) =>
      max(0, min(value - (value * factor).round(), 255));

  static Color shadeColor(Color color, double factor) => Color.fromRGBO(
      shadeValue(color.red, factor),
      shadeValue(color.green, factor),
      shadeValue(color.blue, factor),
      1);
}

Let’s take our color example and look at the result:

color palette
Color values from shade50 (top) to shade900 (bottom).

This is a much better result, which corelates with the color palettes of the Material Design. This implementation is also used by the material_color_generator package.

Photo by guy stevens on Unsplash


Comments

Leave a Reply

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