Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support tone-based surface and surface container ColorScheme roles #115912

Closed
guidezpl opened this issue Nov 23, 2022 · 14 comments · Fixed by #138521
Closed

Support tone-based surface and surface container ColorScheme roles #115912

guidezpl opened this issue Nov 23, 2022 · 14 comments · Fixed by #138521
Assignees
Labels
c: new feature Nothing broken; request for a new capability c: tech-debt Technical debt, code quality, testing, etc. f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. P2 Important issues not at the top of the work list team-design Owned by Design Languages team triaged-design Triaged by Design Languages team

Comments

@guidezpl
Copy link
Member

guidezpl commented Nov 23, 2022

Guidelines: https://m3.material.io/styles/color/the-color-system/color-roles#0abbf8b7-61e1-49ee-9f97-4967beb1e4fe

image

New tone-based colors will replace the existing surface roles. The updated roles remove the use of opacity overlays and thus of surface tint colors. The updated roles are also no longer tied to elevation, offering more flexibility for large screens, and support for future feature development including global user-controlled contrast and color fidelity. Material recommends teams accept these updates across code, tokens, and figma.

The following diagram shows the recommended approach to remapping from the old to the new surfaces.

transition path

Google issue b/266528810 and Google-internal Material Design rationale with FAQ

@guidezpl guidezpl added c: new feature Nothing broken; request for a new capability framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. labels Nov 23, 2022
@guidezpl guidezpl self-assigned this Nov 23, 2022
@guidezpl guidezpl changed the title Support tone-based surfaces Support tone-based surface color roles Apr 14, 2023
@guidezpl guidezpl changed the title Support tone-based surface color roles Support tone-based surface ColorScheme roles Apr 14, 2023
@guidezpl guidezpl removed their assignment Jun 5, 2023
@guidezpl
Copy link
Member Author

guidezpl commented Jun 5, 2023

Tokens are now available for this. It's important to note the updates above are part of version 162 and represent a breaking change corresponding that will need to be carefully prepared. Googlers, see g/material-tokens-announce for more info.

@TahaTesser TahaTesser changed the title Support tone-based surface ColorScheme roles Support tone-based surface and surface container ColorScheme roles Jun 29, 2023
@flutter-triage-bot flutter-triage-bot bot added multiteam-retriage-candidate team-design Owned by Design Languages team triaged-design Triaged by Design Languages team labels Jul 8, 2023
@timbotimbo
Copy link

timbotimbo commented Jul 19, 2023

For anyone looking for a workaround until this gets integrated into Flutter.

You can generate these colors using the material_color_utilites package.
The numbers in the material docs are tone values, N-6 in the image above is the Neutral color with tone 6.

Change the tone of a color

Hct toneColor = Hct.fromInt(Colors.red.value);  // Hct.fromInt(0xFF4285F4)
toneColor.tone = 10;
Color result = Color(toneColor.toInt());

Some quick and dirty code to generate these colors from a seed color:

static Color seedColor = Colors.red;

static Color getSurface(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 6 : 98));
}

static Color getSurfaceDim(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 6 : 87));
}

static Color getSurfaceBright(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 24 : 98));
}

static Color getSurfaceContainerLowest(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 4 : 100));
}

static Color getSurfaceContainerLow(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 10 : 96));
}

static Color getSurfaceContainer(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 12 : 94));
}

static Color getSurfaceContainerHigh(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 17 : 92));
}

static Color getSurfaceContainerHighest(bool darkMode) {
  CorePalette p = CorePalette.of(seedColor.value);
  return Color(p.neutral.get(darkMode ? 22 : 90));
}

@matasb-google
Copy link
Contributor

Is there expected timelines when this will be available?

@guidezpl
Copy link
Member Author

cc @HansMuller @xster

@MuhmdHsn313
Copy link

Any updates?

@amal-stack
Copy link

These changes appear to be part of a wider set of updates to the color system, see this.

Details

Added

The following color roles have been added. See: https://m3.material.io/styles/color/the-color-system/color-roles:

Color Role Tone: Light Tone: Dark
Surface Dim N-87 N-6
Surface Bright N-98 N-24
Surface Container Lowest N-100 N-4
Surface Container Low N-96 N-10
Surface Container N-94 N-12
Surface Container High N-92 N-17
Surface Container Highest N-90 N-22

New fixed accent colors have also been introduced (see #137683).

Changed

  1. Tones for the "Surface" color role have been updated:

    Old

    Color Role Tone: Light Tone: Dark
    Surface N-99 N-10

    New

    Color Role Tone: Light Tone: Dark
    Surface N-98 N-6
  2. The chroma for the neutral palette has been increased from 4 to 6.

Deprecated

The following color roles are marked as legacy/deprecated:

  • Background
  • On background
  • Surface variant

As stated here:

Note: Background is a legacy color role. It is recommended to use Surface instead of Background.

The "On background" and "Surface Variant" roles still exist in the list of tokens but are not shown as part of the color scheme and possibly will be deprecated soon.

@amal-stack
Copy link

Currently, Flutter generates the Material 3 color schemes using ColorScheme.fromSeed which, in turn, uses the Scheme class of the Material Color Utilities package.

However, it appears that the Scheme class will be deprecated soon in favor of DynamicScheme. The make_schemes.md file in the Material Color Utilities GitHub repository has instructions for migrating from Scheme.

@HansMuller
Copy link
Contributor

CC @QuncCccccc, @Piinks

@Piinks Piinks added c: tech-debt Technical debt, code quality, testing, etc. P2 Important issues not at the top of the work list labels Nov 2, 2023
@ElecSmurf
Copy link

Hi, is there an update on this?

Will the flutter ColorScheme class be extended with the different Surface color roles, enabling static setting and fine graded impact on these?

@ivo22dev
Copy link

ivo22dev commented Dec 29, 2023

I created an extension of BuildContext to get all the colors from DynamicScheme, using the material_color_utilites package, just in case it is useful to anyone

extension MaterialColors on BuildContext {
  static final Map<bool, Map<DynamicColor, Color>> _colorCache = {};

  bool get isDarkTheme {
    Brightness brightness = Theme.of(this).brightness;

    if (brightness == Brightness.dark) {
      return true;
    } else {
      return false;
    }
  }

  DynamicScheme _createDynamicScheme() {
    final TonalPalette primaryPalette =
        TonalPalette.fromHct(Hct.fromInt(AppColors.primaryColor.value));
    final TonalPalette secondaryPalette = TonalPalette.fromHct(Hct.fromInt(
        AppColors.secondaryColor?.value ??
            Theme.of(this).colorScheme.secondary.value));
    final TonalPalette tertiaryPalette = TonalPalette.fromHct(Hct.fromInt(
        AppColors.tertiaryColor?.value ??
            Theme.of(this).colorScheme.tertiary.value));
    final TonalPalette neutralPalette = TonalPalette.fromHct(
        Hct.fromInt(Theme.of(this).colorScheme.surface.value));
    final TonalPalette neutralVariantPalette = TonalPalette.fromHct(
        Hct.fromInt(Theme.of(this).colorScheme.surfaceVariant.value));

    return DynamicScheme(
        sourceColorArgb: AppColors.primaryColor.value,
        variant: Variant.vibrant,
        isDark: isDarkTheme,
        primaryPalette: primaryPalette,
        secondaryPalette: secondaryPalette,
        tertiaryPalette: tertiaryPalette,
        neutralPalette: neutralPalette,
        neutralVariantPalette: neutralVariantPalette);
  }

  Color _getColor(DynamicColor colorType) {
    bool isDark =
        isDarkTheme;
    if (_colorCache[isDark]?.containsKey(colorType) ?? false) {
      return _colorCache[isDark]![colorType]!;
    }

    final dynamicScheme = _createDynamicScheme();
    final color = Color(colorType.getArgb(dynamicScheme));
    _colorCache[isDark] ??= {};
    _colorCache[isDark]![colorType] = color;

    return color;
  }

  Color get primary {
    return _getColor(MaterialDynamicColors.primary);
  }

  Color get onPrimary {
    return _getColor(MaterialDynamicColors.onPrimary);
  }

  Color get primaryContainer {
    return _getColor(MaterialDynamicColors.primaryContainer);
  }

  Color get onPrimaryContainer {
    return _getColor(MaterialDynamicColors.onPrimaryContainer);
  }

  Color get primaryFixed {
    return _getColor(MaterialDynamicColors.primaryFixed);
  }

  Color get primaryFixedDim {
    return _getColor(MaterialDynamicColors.primaryFixedDim);
  }

  Color get onPrimaryFixed {
    return _getColor(MaterialDynamicColors.onPrimaryFixed);
  }

  Color get onPrimaryFixedVariant {
    return _getColor(MaterialDynamicColors.onPrimaryFixedVariant);
  }

  // Secondary
  Color get secondary {
    return _getColor(MaterialDynamicColors.secondary);
  }

  Color get onSecondary {
    return _getColor(MaterialDynamicColors.onSecondary);
  }

  Color get secondaryContainer {
    return _getColor(MaterialDynamicColors.secondaryContainer);
  }

  Color get onSecondaryContainer {
    return _getColor(MaterialDynamicColors.onSecondaryContainer);
  }

  Color get secondaryFixed {
    return _getColor(MaterialDynamicColors.secondaryFixed);
  }

  Color get secondaryFixedDim {
    return _getColor(MaterialDynamicColors.secondaryFixedDim);
  }

  Color get onSecondaryFixed {
    return _getColor(MaterialDynamicColors.onSecondaryFixed);
  }

  Color get onSecondaryFixedVariant {
    return _getColor(MaterialDynamicColors.onSecondaryFixedVariant);
  }

  // Tertiary
  Color get tertiary {
    return _getColor(MaterialDynamicColors.tertiary);
  }

  Color get onTertiary {
    return _getColor(MaterialDynamicColors.onTertiary);
  }

  Color get tertiaryContainer {
    return _getColor(MaterialDynamicColors.tertiaryContainer);
  }

  Color get onTertiaryContainer {
    return _getColor(MaterialDynamicColors.onTertiaryContainer);
  }

  Color get tertiaryFixed {
    return _getColor(MaterialDynamicColors.tertiaryFixed);
  }

  Color get tertiaryFixedDim {
    return _getColor(MaterialDynamicColors.tertiaryFixedDim);
  }

  Color get onTertiaryFixed {
    return _getColor(MaterialDynamicColors.onTertiaryFixed);
  }

  Color get onTertiaryFixedVariant {
    return _getColor(MaterialDynamicColors.onTertiaryFixedVariant);
  }

  // Error
  Color get error {
    return _getColor(MaterialDynamicColors.error);
  }

  Color get onError {
    return _getColor(MaterialDynamicColors.onError);
  }

  Color get errorContainer {
    return _getColor(MaterialDynamicColors.errorContainer);
  }

  Color get onErrorContainer {
    return _getColor(MaterialDynamicColors.onErrorContainer);
  }

  // Surface
  Color get surfaceDim {
    return _getColor(MaterialDynamicColors.surfaceDim);
  }

  Color get surface {
    return _getColor(MaterialDynamicColors.surface);
  }

  Color get surfaceBright {
    return _getColor(MaterialDynamicColors.surfaceBright);
  }

  Color get surfaceContainerLowest {
    return _getColor(MaterialDynamicColors.surfaceContainerLowest);
  }

  Color get surfaceContainerLow {
    return _getColor(MaterialDynamicColors.surfaceContainerLow);
  }

  Color get surfaceContainer {
    return _getColor(MaterialDynamicColors.surfaceContainer);
  }

  Color get surfaceContainerHigh {
    return _getColor(MaterialDynamicColors.surfaceContainerHigh);
  }

  Color get surfaceContainerHighest {
    return _getColor(MaterialDynamicColors.surfaceContainerHighest);
  }

  Color get onSurface {
    return _getColor(MaterialDynamicColors.onSurface);
  }

  Color get onSurfaceVariant {
    return _getColor(MaterialDynamicColors.onSurfaceVariant);
  }

  Color get outline {
    return _getColor(MaterialDynamicColors.outline);
  }

  Color get outlineVariant {
    return _getColor(MaterialDynamicColors.outlineVariant);
  }

  Color get inverseSurface {
    return _getColor(MaterialDynamicColors.inverseSurface);
  }

  Color get inverseOnSurface {
    return _getColor(MaterialDynamicColors.inverseOnSurface);
  }

  Color get inversePrimary {
    return _getColor(MaterialDynamicColors.inversePrimary);
  }

  Color get scrim {
    return _getColor(MaterialDynamicColors.scrim);
  }

  Color get shadow {
    return _getColor(MaterialDynamicColors.shadow);
  }
}

@philip-haspa
Copy link

any updates?

@QuncCccccc
Copy link
Contributor

QuncCccccc commented Feb 13, 2024

any updates?

#138521 will be merged soon!

@QuncCccccc
Copy link
Contributor

#144805 adds an enhancement for ColorScheme.fromSeed() so when we input a brighter color with a high chroma and we want the resulting ColorScheme respects the seed color, we can achieve this by setting dynamicSchemeVariant: DynamicSchemeVariant. fidelity or dynamicSchemeVariant: DynamicSchemeVariant. content.

Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
c: new feature Nothing broken; request for a new capability c: tech-debt Technical debt, code quality, testing, etc. f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. P2 Important issues not at the top of the work list team-design Owned by Design Languages team triaged-design Triaged by Design Languages team
Projects
No open projects
Status: 🚧 In Progress
Development

Successfully merging a pull request may close this issue.