From 8aba881085b88624d844420c125ff273921078b7 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:38:39 -0700 Subject: [PATCH 01/11] Update token template & generate M3E typography defaults --- .../generated/typography_defaults_m3e.g.dart | 919 ++++++++++++++++++ .../tool/gen_defaults/bin/gen_defaults.dart | 1 + .../tool/gen_defaults/templates/template.dart | 1 - .../templates/typography_template.dart | 93 +- 4 files changed, 991 insertions(+), 23 deletions(-) create mode 100644 packages/material_ui/lib/src/generated/typography_defaults_m3e.g.dart diff --git a/packages/material_ui/lib/src/generated/typography_defaults_m3e.g.dart b/packages/material_ui/lib/src/generated/typography_defaults_m3e.g.dart new file mode 100644 index 000000000000..30024bb52796 --- /dev/null +++ b/packages/material_ui/lib/src/generated/typography_defaults_m3e.g.dart @@ -0,0 +1,919 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Do not edit by hand. The code is generated from data in the Material +// Design token database by the script: +// packages/material_ui/tool/gen_defaults/bin/gen_defaults.dart. +part of '../typography.dart'; + +abstract final class _M3ETypography { + static const TextTheme englishLike = TextTheme( + displayLarge: TextStyle( + debugLabel: 'englishLike displayLarge 2025', + inherit: false, + fontSize: 57.0, + fontWeight: FontWeight.w400, + letterSpacing: -0.25, + height: 1.12, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayMedium: TextStyle( + debugLabel: 'englishLike displayMedium 2025', + inherit: false, + fontSize: 45.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.16, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displaySmall: TextStyle( + debugLabel: 'englishLike displaySmall 2025', + inherit: false, + fontSize: 36.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.22, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineLarge: TextStyle( + debugLabel: 'englishLike headlineLarge 2025', + inherit: false, + fontSize: 32.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.25, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineMedium: TextStyle( + debugLabel: 'englishLike headlineMedium 2025', + inherit: false, + fontSize: 28.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.29, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineSmall: TextStyle( + debugLabel: 'englishLike headlineSmall 2025', + inherit: false, + fontSize: 24.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleLarge: TextStyle( + debugLabel: 'englishLike titleLarge 2025', + inherit: false, + fontSize: 22.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.27, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleMedium: TextStyle( + debugLabel: 'englishLike titleMedium 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.15, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleSmall: TextStyle( + debugLabel: 'englishLike titleSmall 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelLarge: TextStyle( + debugLabel: 'englishLike labelLarge 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelMedium: TextStyle( + debugLabel: 'englishLike labelMedium 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelSmall: TextStyle( + debugLabel: 'englishLike labelSmall 2025', + inherit: false, + fontSize: 11.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.45, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyLarge: TextStyle( + debugLabel: 'englishLike bodyLarge 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.5, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyMedium: TextStyle( + debugLabel: 'englishLike bodyMedium 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.25, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodySmall: TextStyle( + debugLabel: 'englishLike bodySmall 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.4, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayLargeEmphasized: TextStyle( + debugLabel: 'englishLike displayLargeEmphasized 2025', + inherit: false, + fontSize: 57.0, + fontWeight: FontWeight.w500, + letterSpacing: -0.25, + height: 1.12, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayMediumEmphasized: TextStyle( + debugLabel: 'englishLike displayMediumEmphasized 2025', + inherit: false, + fontSize: 45.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.16, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displaySmallEmphasized: TextStyle( + debugLabel: 'englishLike displaySmallEmphasized 2025', + inherit: false, + fontSize: 36.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.22, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineLargeEmphasized: TextStyle( + debugLabel: 'englishLike headlineLargeEmphasized 2025', + inherit: false, + fontSize: 32.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.25, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineMediumEmphasized: TextStyle( + debugLabel: 'englishLike headlineMediumEmphasized 2025', + inherit: false, + fontSize: 28.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.29, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineSmallEmphasized: TextStyle( + debugLabel: 'englishLike headlineSmallEmphasized 2025', + inherit: false, + fontSize: 24.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleLargeEmphasized: TextStyle( + debugLabel: 'englishLike titleLargeEmphasized 2025', + inherit: false, + fontSize: 22.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.27, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleMediumEmphasized: TextStyle( + debugLabel: 'englishLike titleMediumEmphasized 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.15, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleSmallEmphasized: TextStyle( + debugLabel: 'englishLike titleSmallEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelLargeEmphasized: TextStyle( + debugLabel: 'englishLike labelLargeEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelMediumEmphasized: TextStyle( + debugLabel: 'englishLike labelMediumEmphasized 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelSmallEmphasized: TextStyle( + debugLabel: 'englishLike labelSmallEmphasized 2025', + inherit: false, + fontSize: 11.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + height: 1.45, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyLargeEmphasized: TextStyle( + debugLabel: 'englishLike bodyLargeEmphasized 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyMediumEmphasized: TextStyle( + debugLabel: 'englishLike bodyMediumEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.25, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodySmallEmphasized: TextStyle( + debugLabel: 'englishLike bodySmallEmphasized 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.4, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + ); + + static const TextTheme dense = TextTheme( + displayLarge: TextStyle( + debugLabel: 'dense displayLarge 2025', + inherit: false, + fontSize: 57.0, + fontWeight: FontWeight.w400, + letterSpacing: -0.25, + height: 1.12, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayMedium: TextStyle( + debugLabel: 'dense displayMedium 2025', + inherit: false, + fontSize: 45.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.16, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + displaySmall: TextStyle( + debugLabel: 'dense displaySmall 2025', + inherit: false, + fontSize: 36.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.22, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineLarge: TextStyle( + debugLabel: 'dense headlineLarge 2025', + inherit: false, + fontSize: 32.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.25, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineMedium: TextStyle( + debugLabel: 'dense headlineMedium 2025', + inherit: false, + fontSize: 28.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.29, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineSmall: TextStyle( + debugLabel: 'dense headlineSmall 2025', + inherit: false, + fontSize: 24.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.33, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleLarge: TextStyle( + debugLabel: 'dense titleLarge 2025', + inherit: false, + fontSize: 22.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.27, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleMedium: TextStyle( + debugLabel: 'dense titleMedium 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.15, + height: 1.50, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleSmall: TextStyle( + debugLabel: 'dense titleSmall 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelLarge: TextStyle( + debugLabel: 'dense labelLarge 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelMedium: TextStyle( + debugLabel: 'dense labelMedium 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.33, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelSmall: TextStyle( + debugLabel: 'dense labelSmall 2025', + inherit: false, + fontSize: 11.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.45, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyLarge: TextStyle( + debugLabel: 'dense bodyLarge 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.5, + height: 1.50, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyMedium: TextStyle( + debugLabel: 'dense bodyMedium 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.25, + height: 1.43, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodySmall: TextStyle( + debugLabel: 'dense bodySmall 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.4, + height: 1.33, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayLargeEmphasized: TextStyle( + debugLabel: 'dense displayLargeEmphasized 2025', + inherit: false, + fontSize: 57.0, + fontWeight: FontWeight.w500, + letterSpacing: -0.25, + height: 1.12, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayMediumEmphasized: TextStyle( + debugLabel: 'dense displayMediumEmphasized 2025', + inherit: false, + fontSize: 45.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.16, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + displaySmallEmphasized: TextStyle( + debugLabel: 'dense displaySmallEmphasized 2025', + inherit: false, + fontSize: 36.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.22, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineLargeEmphasized: TextStyle( + debugLabel: 'dense headlineLargeEmphasized 2025', + inherit: false, + fontSize: 32.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.25, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineMediumEmphasized: TextStyle( + debugLabel: 'dense headlineMediumEmphasized 2025', + inherit: false, + fontSize: 28.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.29, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineSmallEmphasized: TextStyle( + debugLabel: 'dense headlineSmallEmphasized 2025', + inherit: false, + fontSize: 24.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.33, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleLargeEmphasized: TextStyle( + debugLabel: 'dense titleLargeEmphasized 2025', + inherit: false, + fontSize: 22.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.27, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleMediumEmphasized: TextStyle( + debugLabel: 'dense titleMediumEmphasized 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.15, + height: 1.50, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleSmallEmphasized: TextStyle( + debugLabel: 'dense titleSmallEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelLargeEmphasized: TextStyle( + debugLabel: 'dense labelLargeEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelMediumEmphasized: TextStyle( + debugLabel: 'dense labelMediumEmphasized 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + height: 1.33, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelSmallEmphasized: TextStyle( + debugLabel: 'dense labelSmallEmphasized 2025', + inherit: false, + fontSize: 11.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + height: 1.45, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyLargeEmphasized: TextStyle( + debugLabel: 'dense bodyLargeEmphasized 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.50, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyMediumEmphasized: TextStyle( + debugLabel: 'dense bodyMediumEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.25, + height: 1.43, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodySmallEmphasized: TextStyle( + debugLabel: 'dense bodySmallEmphasized 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.4, + height: 1.33, + textBaseline: TextBaseline.ideographic, + leadingDistribution: TextLeadingDistribution.even, + ), + ); + + static const TextTheme tall = TextTheme( + displayLarge: TextStyle( + debugLabel: 'tall displayLarge 2025', + inherit: false, + fontSize: 57.0, + fontWeight: FontWeight.w400, + letterSpacing: -0.25, + height: 1.12, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayMedium: TextStyle( + debugLabel: 'tall displayMedium 2025', + inherit: false, + fontSize: 45.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.16, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displaySmall: TextStyle( + debugLabel: 'tall displaySmall 2025', + inherit: false, + fontSize: 36.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.22, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineLarge: TextStyle( + debugLabel: 'tall headlineLarge 2025', + inherit: false, + fontSize: 32.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.25, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineMedium: TextStyle( + debugLabel: 'tall headlineMedium 2025', + inherit: false, + fontSize: 28.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.29, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineSmall: TextStyle( + debugLabel: 'tall headlineSmall 2025', + inherit: false, + fontSize: 24.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleLarge: TextStyle( + debugLabel: 'tall titleLarge 2025', + inherit: false, + fontSize: 22.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.0, + height: 1.27, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleMedium: TextStyle( + debugLabel: 'tall titleMedium 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.15, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleSmall: TextStyle( + debugLabel: 'tall titleSmall 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelLarge: TextStyle( + debugLabel: 'tall labelLarge 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelMedium: TextStyle( + debugLabel: 'tall labelMedium 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelSmall: TextStyle( + debugLabel: 'tall labelSmall 2025', + inherit: false, + fontSize: 11.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.45, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyLarge: TextStyle( + debugLabel: 'tall bodyLarge 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.5, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyMedium: TextStyle( + debugLabel: 'tall bodyMedium 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.25, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodySmall: TextStyle( + debugLabel: 'tall bodySmall 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w400, + letterSpacing: 0.4, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayLargeEmphasized: TextStyle( + debugLabel: 'tall displayLargeEmphasized 2025', + inherit: false, + fontSize: 57.0, + fontWeight: FontWeight.w500, + letterSpacing: -0.25, + height: 1.12, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displayMediumEmphasized: TextStyle( + debugLabel: 'tall displayMediumEmphasized 2025', + inherit: false, + fontSize: 45.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.16, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + displaySmallEmphasized: TextStyle( + debugLabel: 'tall displaySmallEmphasized 2025', + inherit: false, + fontSize: 36.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.22, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineLargeEmphasized: TextStyle( + debugLabel: 'tall headlineLargeEmphasized 2025', + inherit: false, + fontSize: 32.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.25, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineMediumEmphasized: TextStyle( + debugLabel: 'tall headlineMediumEmphasized 2025', + inherit: false, + fontSize: 28.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.29, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + headlineSmallEmphasized: TextStyle( + debugLabel: 'tall headlineSmallEmphasized 2025', + inherit: false, + fontSize: 24.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleLargeEmphasized: TextStyle( + debugLabel: 'tall titleLargeEmphasized 2025', + inherit: false, + fontSize: 22.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.0, + height: 1.27, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleMediumEmphasized: TextStyle( + debugLabel: 'tall titleMediumEmphasized 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.15, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + titleSmallEmphasized: TextStyle( + debugLabel: 'tall titleSmallEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelLargeEmphasized: TextStyle( + debugLabel: 'tall labelLargeEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.1, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelMediumEmphasized: TextStyle( + debugLabel: 'tall labelMediumEmphasized 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + labelSmallEmphasized: TextStyle( + debugLabel: 'tall labelSmallEmphasized 2025', + inherit: false, + fontSize: 11.0, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + height: 1.45, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyLargeEmphasized: TextStyle( + debugLabel: 'tall bodyLargeEmphasized 2025', + inherit: false, + fontSize: 16.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.5, + height: 1.50, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodyMediumEmphasized: TextStyle( + debugLabel: 'tall bodyMediumEmphasized 2025', + inherit: false, + fontSize: 14.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.25, + height: 1.43, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + bodySmallEmphasized: TextStyle( + debugLabel: 'tall bodySmallEmphasized 2025', + inherit: false, + fontSize: 12.0, + fontWeight: FontWeight.w500, + letterSpacing: 0.4, + height: 1.33, + textBaseline: TextBaseline.alphabetic, + leadingDistribution: TextLeadingDistribution.even, + ), + ); +} diff --git a/packages/material_ui/tool/gen_defaults/bin/gen_defaults.dart b/packages/material_ui/tool/gen_defaults/bin/gen_defaults.dart index a8e6b8b4b509..4b2874e7d46d 100644 --- a/packages/material_ui/tool/gen_defaults/bin/gen_defaults.dart +++ b/packages/material_ui/tool/gen_defaults/bin/gen_defaults.dart @@ -120,4 +120,5 @@ Future main(List args) async { const TextFieldTemplateM3().generateFile(verbose: verbose); // const TimePickerTemplateM3().generateFile(verbose: verbose); const TypographyTemplateM3().generateFile(verbose: verbose); + const TypographyTemplateM3E().generateFile(verbose: verbose); } diff --git a/packages/material_ui/tool/gen_defaults/templates/template.dart b/packages/material_ui/tool/gen_defaults/templates/template.dart index c7518d47e537..ed814565f914 100644 --- a/packages/material_ui/tool/gen_defaults/templates/template.dart +++ b/packages/material_ui/tool/gen_defaults/templates/template.dart @@ -28,7 +28,6 @@ abstract class TokenTemplateM3E extends TokenTemplate { _MaterialVersion get _version => _MaterialVersion.material3Expressive; } -@visibleForTesting abstract class TokenTemplate { const TokenTemplate(); diff --git a/packages/material_ui/tool/gen_defaults/templates/typography_template.dart b/packages/material_ui/tool/gen_defaults/templates/typography_template.dart index 1b9ed517e68b..d91fd0257360 100644 --- a/packages/material_ui/tool/gen_defaults/templates/typography_template.dart +++ b/packages/material_ui/tool/gen_defaults/templates/typography_template.dart @@ -3,38 +3,56 @@ // found in the LICENSE file. import '../data/typescale.dart'; +import '../data/typescale_emphasized.dart'; import '../data/typescale_struct.dart'; import 'template.dart'; -class TypographyTemplateM3 extends TokenTemplateM3 { - const TypographyTemplateM3(); +const List<(String, TypescaleStruct)> _baseTextStyles = <(String, TypescaleStruct)>[ + ('displayLarge', TokenTypescale.displayLarge), + ('displayMedium', TokenTypescale.displayMedium), + ('displaySmall', TokenTypescale.displaySmall), + ('headlineLarge', TokenTypescale.headlineLarge), + ('headlineMedium', TokenTypescale.headlineMedium), + ('headlineSmall', TokenTypescale.headlineSmall), + ('titleLarge', TokenTypescale.titleLarge), + ('titleMedium', TokenTypescale.titleMedium), + ('titleSmall', TokenTypescale.titleSmall), + ('labelLarge', TokenTypescale.labelLarge), + ('labelMedium', TokenTypescale.labelMedium), + ('labelSmall', TokenTypescale.labelSmall), + ('bodyLarge', TokenTypescale.bodyLarge), + ('bodyMedium', TokenTypescale.bodyMedium), + ('bodySmall', TokenTypescale.bodySmall), +]; + +const List<(String, TypescaleStruct)> _emphasizedTextStyles = <(String, TypescaleStruct)>[ + ('displayLargeEmphasized', TokenTypescaleEmphasized.displayLarge), + ('displayMediumEmphasized', TokenTypescaleEmphasized.displayMedium), + ('displaySmallEmphasized', TokenTypescaleEmphasized.displaySmall), + ('headlineLargeEmphasized', TokenTypescaleEmphasized.headlineLarge), + ('headlineMediumEmphasized', TokenTypescaleEmphasized.headlineMedium), + ('headlineSmallEmphasized', TokenTypescaleEmphasized.headlineSmall), + ('titleLargeEmphasized', TokenTypescaleEmphasized.titleLarge), + ('titleMediumEmphasized', TokenTypescaleEmphasized.titleMedium), + ('titleSmallEmphasized', TokenTypescaleEmphasized.titleSmall), + ('labelLargeEmphasized', TokenTypescaleEmphasized.labelLarge), + ('labelMediumEmphasized', TokenTypescaleEmphasized.labelMedium), + ('labelSmallEmphasized', TokenTypescaleEmphasized.labelSmall), + ('bodyLargeEmphasized', TokenTypescaleEmphasized.bodyLarge), + ('bodyMediumEmphasized', TokenTypescaleEmphasized.bodyMedium), + ('bodySmallEmphasized', TokenTypescaleEmphasized.bodySmall), +]; +mixin TypographyTokenMixin on TokenTemplate { @override String get name => 'Typography'; @override String get parentFilePath => 'typography.dart'; - @override - String get className => '_M3Typography'; + String get _typographyYear; - static const List<(String, TypescaleStruct)> _textStyles = <(String, TypescaleStruct)>[ - ('displayLarge', TokenTypescale.displayLarge), - ('displayMedium', TokenTypescale.displayMedium), - ('displaySmall', TokenTypescale.displaySmall), - ('headlineLarge', TokenTypescale.headlineLarge), - ('headlineMedium', TokenTypescale.headlineMedium), - ('headlineSmall', TokenTypescale.headlineSmall), - ('titleLarge', TokenTypescale.titleLarge), - ('titleMedium', TokenTypescale.titleMedium), - ('titleSmall', TokenTypescale.titleSmall), - ('labelLarge', TokenTypescale.labelLarge), - ('labelMedium', TokenTypescale.labelMedium), - ('labelSmall', TokenTypescale.labelSmall), - ('bodyLarge', TokenTypescale.bodyLarge), - ('bodyMedium', TokenTypescale.bodyMedium), - ('bodySmall', TokenTypescale.bodySmall), - ]; + List<(String, TypescaleStruct)> get _textStyles; @override String generateContents(String className) => @@ -51,7 +69,9 @@ abstract final class $className { String _textTheme(String name, String baseline) { final theme = StringBuffer('static const TextTheme $name = TextTheme(\n'); for (final (String styleName, TypescaleStruct token) in _textStyles) { - theme.writeln(' $styleName: ${_textStyleDef(token, '$name $styleName 2021', baseline)},'); + theme.writeln( + ' $styleName: ${_textStyleDef(token, '$name $styleName $_typographyYear', baseline)},', + ); } theme.write(' );'); return theme.toString(); @@ -89,3 +109,32 @@ abstract final class $className { return (lineHeight / size).toStringAsFixed(2); } } + +class TypographyTemplateM3 extends TokenTemplateM3 with TypographyTokenMixin { + const TypographyTemplateM3(); + + @override + String get className => '_M3Typography'; + + @override + String get _typographyYear => '2021'; + + @override + List<(String, TypescaleStruct)> get _textStyles => _baseTextStyles; +} + +class TypographyTemplateM3E extends TokenTemplateM3E with TypographyTokenMixin { + const TypographyTemplateM3E(); + + @override + String get className => '_M3ETypography'; + + @override + String get _typographyYear => '2025'; + + @override + List<(String, TypescaleStruct)> get _textStyles => <(String, TypescaleStruct)>[ + ..._baseTextStyles, + ..._emphasizedTextStyles, + ]; +} From d85687f271538d09ab9667f912af1dd0a39f7ab3 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 13:31:39 -0700 Subject: [PATCH 02/11] Implement Typography.material2025 factory constructor and emphasized text styles --- packages/material_ui/lib/src/text_theme.dart | 386 +++++++++++- packages/material_ui/lib/src/typography.dart | 625 ++++++++++++++++++- 2 files changed, 975 insertions(+), 36 deletions(-) diff --git a/packages/material_ui/lib/src/text_theme.dart b/packages/material_ui/lib/src/text_theme.dart index 80e9c0d18a54..c8482f22c9c6 100644 --- a/packages/material_ui/lib/src/text_theme.dart +++ b/packages/material_ui/lib/src/text_theme.dart @@ -32,9 +32,13 @@ import 'theme.dart'; /// ![](https://lh3.googleusercontent.com/Yvngs5mQSjXa_9T4X3JDucO62c5hdZHPDa7qeRH6DsJQvGr_q7EBrTkhkPiQd9OeR1v_Uk38Cjd9nUpP3nevDyHpKWuXSfQ1Gq78bOnBN7sr=s0) /// /// The Material Design typography scheme was significantly changed in the -/// current (2021) version of the specification +/// Material 3 (2021) version of the specification. /// ([https://m3.material.io/styles/typography/tokens](https://m3.material.io/styles/typography/tokens)). /// +/// The current Material 3 Expressive (2025) version of the specification further +/// expanded upon the Material 3 (2021) version, with the addition of an +/// emphasized variant for each of the 2021 text styles. +/// /// The **2021** spec has fifteen text styles: /// /// | NAME | SIZE | HEIGHT | WEIGHT | SPACING | | @@ -57,6 +61,37 @@ import 'theme.dart'; /// /// ...where "regular" is `FontWeight.w400` and "medium" is `FontWeight.w500`. /// +/// The Material 3 Expressive (**2025**) spec adds an emphasized variant for +/// each of the fifteen 2021 text styles (for example, [titleMediumEmphasized] +/// for [titleMedium]). Emphasized styles share the size, height and spacing of +/// their baseline style, but use a heavier weight: +/// +/// | NAME | SIZE | HEIGHT | WEIGHT | SPACING | | +/// |--------------------------|------|---------|---------|----------|-------| +/// | displayLargeEmphasized | 57.0 | 64.0 | medium | -0.25 | | +/// | displayMediumEmphasized | 45.0 | 52.0 | medium | 0.0 | | +/// | displaySmallEmphasized | 36.0 | 44.0 | medium | 0.0 | | +/// | headlineLargeEmphasized | 32.0 | 40.0 | medium | 0.0 | | +/// | headlineMediumEmphasized | 28.0 | 36.0 | medium | 0.0 | | +/// | headlineSmallEmphasized | 24.0 | 32.0 | medium | 0.0 | | +/// | titleLargeEmphasized | 22.0 | 28.0 | medium | 0.0 | | +/// | titleMediumEmphasized | 16.0 | 24.0 | bold | 0.15 | | +/// | titleSmallEmphasized | 14.0 | 20.0 | bold | 0.1 | | +/// | bodyLargeEmphasized | 16.0 | 24.0 | medium | 0.5 | | +/// | bodyMediumEmphasized | 14.0 | 20.0 | medium | 0.25 | | +/// | bodySmallEmphasized | 12.0 | 16.0 | medium | 0.4 | | +/// | labelLargeEmphasized | 14.0 | 20.0 | bold | 0.1 | | +/// | labelMediumEmphasized | 12.0 | 16.0 | bold | 0.5 | | +/// | labelSmallEmphasized | 11.0 | 16.0 | bold | 0.5 | | +/// +/// ...where "medium" is `FontWeight.w500` and "bold" is `FontWeight.w700`. +/// +/// Emphasized styles are intended for bold, selected, or otherwise emphasized +/// text. They complement the baseline styles, and Material components don't +/// use them by default. The emphasized styles are only populated when +/// [ThemeData.typography] is created with [Typography.material2025]; otherwise +/// they are null. +/// /// The names of the 2018 TextTheme properties match this table from the /// [Material Design spec](https://material.io/design/typography/the-type-system.html#type-scale) /// with a few exceptions: the styles called H1-H6 in the spec are @@ -85,16 +120,19 @@ import 'theme.dart'; /// ...where "light" is `FontWeight.w300`, "regular" is `FontWeight.w400` and /// "medium" is `FontWeight.w500`. /// -/// By default, text styles are initialized to match the 2018 Material Design -/// specification as listed above. To provide backwards compatibility, the 2014 -/// specification is also available. +/// By default, [ThemeData.typography] is initialized to match the 2025 Material +/// Design 3 Expressive specification ([Typography.material2025]). This is +/// equivalent to the 2021 specification for all baseline styles, but also +/// includes the emphasized text styles by default. +/// +/// To explicitly configure a [Theme] with the 2021 Material Design 3 +/// specification, initialize [ThemeData.typography] using +/// [Typography.material2021]. /// -/// To explicitly configure a [Theme] for the 2018 sizes, weights, and letter -/// spacings, you can initialize its [ThemeData.typography] value using -/// [Typography.material2018]. The [Typography] constructor defaults to this -/// configuration. To configure a [Theme] for the 2014 sizes, weights, and letter -/// spacings, initialize its [ThemeData.typography] value using -/// [Typography.material2014]. +/// To provide backwards compatibility, the 2018 and 2014 specifications are +/// also available via [Typography.material2018] (which the [Typography.new] +/// constructor defaults to) and [Typography.material2014] (which +/// [ThemeData.typography] defaults to when [ThemeData.useMaterial3] is false). /// /// See also: /// @@ -115,7 +153,7 @@ class TextTheme with Diagnosticable { /// If you do decide to create your own text theme, consider using one of /// those predefined themes as a starting point for [copyWith] or [apply]. /// - /// The 2018 styles cannot be mixed with the 2021 styles. Only one or the + /// The 2018 styles cannot be mixed with the 2021/2025 styles. Only one or the /// other is allowed in this constructor. The 2018 styles are deprecated and /// will eventually be removed. const TextTheme({ @@ -134,6 +172,21 @@ class TextTheme with Diagnosticable { this.labelLarge, this.labelMedium, this.labelSmall, + this.displayLargeEmphasized, + this.displayMediumEmphasized, + this.displaySmallEmphasized, + this.headlineLargeEmphasized, + this.headlineMediumEmphasized, + this.headlineSmallEmphasized, + this.titleLargeEmphasized, + this.titleMediumEmphasized, + this.titleSmallEmphasized, + this.bodyLargeEmphasized, + this.bodyMediumEmphasized, + this.bodySmallEmphasized, + this.labelLargeEmphasized, + this.labelMediumEmphasized, + this.labelSmallEmphasized, }); /// Largest of the display styles. @@ -230,6 +283,156 @@ class TextTheme with Diagnosticable { /// content body, like captions. final TextStyle? labelSmall; + /// The emphasized variant of [displayLarge]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? displayLargeEmphasized; + + /// The emphasized variant of [displayMedium]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? displayMediumEmphasized; + + /// The emphasized variant of [displaySmall]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? displaySmallEmphasized; + + /// The emphasized variant of [headlineLarge]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? headlineLargeEmphasized; + + /// The emphasized variant of [headlineMedium]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? headlineMediumEmphasized; + + /// The emphasized variant of [headlineSmall]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? headlineSmallEmphasized; + + /// The emphasized variant of [titleLarge]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? titleLargeEmphasized; + + /// The emphasized variant of [titleMedium]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? titleMediumEmphasized; + + /// The emphasized variant of [titleSmall]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? titleSmallEmphasized; + + /// The emphasized variant of [bodyLarge]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? bodyLargeEmphasized; + + /// The emphasized variant of [bodyMedium]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? bodyMediumEmphasized; + + /// The emphasized variant of [bodySmall]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? bodySmallEmphasized; + + /// The emphasized variant of [labelLarge]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? labelLargeEmphasized; + + /// The emphasized variant of [labelMedium]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? labelMediumEmphasized; + + /// The emphasized variant of [labelSmall]. + /// + /// Emphasized styles are intended for bold, selected, or otherwise emphasized + /// text. They complement the baseline styles, and Material components don't + /// use them by default. + /// + /// This is only provided by default when [ThemeData.typography] is + /// [Typography.material2025]. + final TextStyle? labelSmallEmphasized; + /// Creates a copy of this text theme but with the given fields replaced with /// the new values. /// @@ -290,6 +493,21 @@ class TextTheme with Diagnosticable { TextStyle? labelLarge, TextStyle? labelMedium, TextStyle? labelSmall, + TextStyle? displayLargeEmphasized, + TextStyle? displayMediumEmphasized, + TextStyle? displaySmallEmphasized, + TextStyle? headlineLargeEmphasized, + TextStyle? headlineMediumEmphasized, + TextStyle? headlineSmallEmphasized, + TextStyle? titleLargeEmphasized, + TextStyle? titleMediumEmphasized, + TextStyle? titleSmallEmphasized, + TextStyle? bodyLargeEmphasized, + TextStyle? bodyMediumEmphasized, + TextStyle? bodySmallEmphasized, + TextStyle? labelLargeEmphasized, + TextStyle? labelMediumEmphasized, + TextStyle? labelSmallEmphasized, }) { return TextTheme( displayLarge: displayLarge ?? this.displayLarge, @@ -307,6 +525,21 @@ class TextTheme with Diagnosticable { labelLarge: labelLarge ?? this.labelLarge, labelMedium: labelMedium ?? this.labelMedium, labelSmall: labelSmall ?? this.labelSmall, + displayLargeEmphasized: displayLargeEmphasized ?? this.displayLargeEmphasized, + displayMediumEmphasized: displayMediumEmphasized ?? this.displayMediumEmphasized, + displaySmallEmphasized: displaySmallEmphasized ?? this.displaySmallEmphasized, + headlineLargeEmphasized: headlineLargeEmphasized ?? this.headlineLargeEmphasized, + headlineMediumEmphasized: headlineMediumEmphasized ?? this.headlineMediumEmphasized, + headlineSmallEmphasized: headlineSmallEmphasized ?? this.headlineSmallEmphasized, + titleLargeEmphasized: titleLargeEmphasized ?? this.titleLargeEmphasized, + titleMediumEmphasized: titleMediumEmphasized ?? this.titleMediumEmphasized, + titleSmallEmphasized: titleSmallEmphasized ?? this.titleSmallEmphasized, + bodyLargeEmphasized: bodyLargeEmphasized ?? this.bodyLargeEmphasized, + bodyMediumEmphasized: bodyMediumEmphasized ?? this.bodyMediumEmphasized, + bodySmallEmphasized: bodySmallEmphasized ?? this.bodySmallEmphasized, + labelLargeEmphasized: labelLargeEmphasized ?? this.labelLargeEmphasized, + labelMediumEmphasized: labelMediumEmphasized ?? this.labelMediumEmphasized, + labelSmallEmphasized: labelSmallEmphasized ?? this.labelSmallEmphasized, ); } @@ -389,6 +622,24 @@ class TextTheme with Diagnosticable { labelLarge: mergeStyles(labelLarge, other.labelLarge), labelMedium: mergeStyles(labelMedium, other.labelMedium), labelSmall: mergeStyles(labelSmall, other.labelSmall), + displayLargeEmphasized: mergeStyles(displayLargeEmphasized, other.displayLargeEmphasized), + displayMediumEmphasized: mergeStyles(displayMediumEmphasized, other.displayMediumEmphasized), + displaySmallEmphasized: mergeStyles(displaySmallEmphasized, other.displaySmallEmphasized), + headlineLargeEmphasized: mergeStyles(headlineLargeEmphasized, other.headlineLargeEmphasized), + headlineMediumEmphasized: mergeStyles( + headlineMediumEmphasized, + other.headlineMediumEmphasized, + ), + headlineSmallEmphasized: mergeStyles(headlineSmallEmphasized, other.headlineSmallEmphasized), + titleLargeEmphasized: mergeStyles(titleLargeEmphasized, other.titleLargeEmphasized), + titleMediumEmphasized: mergeStyles(titleMediumEmphasized, other.titleMediumEmphasized), + titleSmallEmphasized: mergeStyles(titleSmallEmphasized, other.titleSmallEmphasized), + bodyLargeEmphasized: mergeStyles(bodyLargeEmphasized, other.bodyLargeEmphasized), + bodyMediumEmphasized: mergeStyles(bodyMediumEmphasized, other.bodyMediumEmphasized), + bodySmallEmphasized: mergeStyles(bodySmallEmphasized, other.bodySmallEmphasized), + labelLargeEmphasized: mergeStyles(labelLargeEmphasized, other.labelLargeEmphasized), + labelMediumEmphasized: mergeStyles(labelMediumEmphasized, other.labelMediumEmphasized), + labelSmallEmphasized: mergeStyles(labelSmallEmphasized, other.labelSmallEmphasized), ); } @@ -397,7 +648,9 @@ class TextTheme with Diagnosticable { /// /// The `displayColor` is applied to [displayLarge], [displayMedium], /// [displaySmall], [headlineLarge], [headlineMedium], and [bodySmall]. The - /// `bodyColor` is applied to the remaining text styles. + /// `bodyColor` is applied to the remaining text styles. Each emphasized + /// style (e.g. [displayLargeEmphasized]) receives the same color as its + /// baseline style. /// /// Consider using [Typography.black] or [Typography.white], which implement /// the typography styles in the Material Design specification, as a starting @@ -458,6 +711,21 @@ class TextTheme with Diagnosticable { labelLarge: applyTo(labelLarge, bodyColor), labelMedium: applyTo(labelMedium, bodyColor), labelSmall: applyTo(labelSmall, bodyColor), + displayLargeEmphasized: applyTo(displayLargeEmphasized, displayColor), + displayMediumEmphasized: applyTo(displayMediumEmphasized, displayColor), + displaySmallEmphasized: applyTo(displaySmallEmphasized, displayColor), + headlineLargeEmphasized: applyTo(headlineLargeEmphasized, displayColor), + headlineMediumEmphasized: applyTo(headlineMediumEmphasized, displayColor), + headlineSmallEmphasized: applyTo(headlineSmallEmphasized, bodyColor), + titleLargeEmphasized: applyTo(titleLargeEmphasized, bodyColor), + titleMediumEmphasized: applyTo(titleMediumEmphasized, bodyColor), + titleSmallEmphasized: applyTo(titleSmallEmphasized, bodyColor), + bodyLargeEmphasized: applyTo(bodyLargeEmphasized, bodyColor), + bodyMediumEmphasized: applyTo(bodyMediumEmphasized, bodyColor), + bodySmallEmphasized: applyTo(bodySmallEmphasized, displayColor), + labelLargeEmphasized: applyTo(labelLargeEmphasized, bodyColor), + labelMediumEmphasized: applyTo(labelMediumEmphasized, bodyColor), + labelSmallEmphasized: applyTo(labelSmallEmphasized, bodyColor), ); } @@ -484,6 +752,45 @@ class TextTheme with Diagnosticable { labelLarge: TextStyle.lerp(a?.labelLarge, b?.labelLarge, t), labelMedium: TextStyle.lerp(a?.labelMedium, b?.labelMedium, t), labelSmall: TextStyle.lerp(a?.labelSmall, b?.labelSmall, t), + displayLargeEmphasized: TextStyle.lerp( + a?.displayLargeEmphasized, + b?.displayLargeEmphasized, + t, + ), + displayMediumEmphasized: TextStyle.lerp( + a?.displayMediumEmphasized, + b?.displayMediumEmphasized, + t, + ), + displaySmallEmphasized: TextStyle.lerp( + a?.displaySmallEmphasized, + b?.displaySmallEmphasized, + t, + ), + headlineLargeEmphasized: TextStyle.lerp( + a?.headlineLargeEmphasized, + b?.headlineLargeEmphasized, + t, + ), + headlineMediumEmphasized: TextStyle.lerp( + a?.headlineMediumEmphasized, + b?.headlineMediumEmphasized, + t, + ), + headlineSmallEmphasized: TextStyle.lerp( + a?.headlineSmallEmphasized, + b?.headlineSmallEmphasized, + t, + ), + titleLargeEmphasized: TextStyle.lerp(a?.titleLargeEmphasized, b?.titleLargeEmphasized, t), + titleMediumEmphasized: TextStyle.lerp(a?.titleMediumEmphasized, b?.titleMediumEmphasized, t), + titleSmallEmphasized: TextStyle.lerp(a?.titleSmallEmphasized, b?.titleSmallEmphasized, t), + bodyLargeEmphasized: TextStyle.lerp(a?.bodyLargeEmphasized, b?.bodyLargeEmphasized, t), + bodyMediumEmphasized: TextStyle.lerp(a?.bodyMediumEmphasized, b?.bodyMediumEmphasized, t), + bodySmallEmphasized: TextStyle.lerp(a?.bodySmallEmphasized, b?.bodySmallEmphasized, t), + labelLargeEmphasized: TextStyle.lerp(a?.labelLargeEmphasized, b?.labelLargeEmphasized, t), + labelMediumEmphasized: TextStyle.lerp(a?.labelMediumEmphasized, b?.labelMediumEmphasized, t), + labelSmallEmphasized: TextStyle.lerp(a?.labelSmallEmphasized, b?.labelSmallEmphasized, t), ); } @@ -529,11 +836,26 @@ class TextTheme with Diagnosticable { bodySmall == other.bodySmall && labelLarge == other.labelLarge && labelMedium == other.labelMedium && - labelSmall == other.labelSmall; + labelSmall == other.labelSmall && + displayLargeEmphasized == other.displayLargeEmphasized && + displayMediumEmphasized == other.displayMediumEmphasized && + displaySmallEmphasized == other.displaySmallEmphasized && + headlineLargeEmphasized == other.headlineLargeEmphasized && + headlineMediumEmphasized == other.headlineMediumEmphasized && + headlineSmallEmphasized == other.headlineSmallEmphasized && + titleLargeEmphasized == other.titleLargeEmphasized && + titleMediumEmphasized == other.titleMediumEmphasized && + titleSmallEmphasized == other.titleSmallEmphasized && + bodyLargeEmphasized == other.bodyLargeEmphasized && + bodyMediumEmphasized == other.bodyMediumEmphasized && + bodySmallEmphasized == other.bodySmallEmphasized && + labelLargeEmphasized == other.labelLargeEmphasized && + labelMediumEmphasized == other.labelMediumEmphasized && + labelSmallEmphasized == other.labelSmallEmphasized; } @override - int get hashCode => Object.hash( + int get hashCode => Object.hashAll([ displayLarge, displayMedium, displaySmall, @@ -549,7 +871,22 @@ class TextTheme with Diagnosticable { labelLarge, labelMedium, labelSmall, - ); + displayLargeEmphasized, + displayMediumEmphasized, + displaySmallEmphasized, + headlineLargeEmphasized, + headlineMediumEmphasized, + headlineSmallEmphasized, + titleLargeEmphasized, + titleMediumEmphasized, + titleSmallEmphasized, + bodyLargeEmphasized, + bodyMediumEmphasized, + bodySmallEmphasized, + labelLargeEmphasized, + labelMediumEmphasized, + labelSmallEmphasized, + ]); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -582,5 +919,24 @@ class TextTheme with Diagnosticable { add('labelLarge', labelLarge, defaultTheme.labelLarge); add('labelMedium', labelMedium, defaultTheme.labelMedium); add('labelSmall', labelSmall, defaultTheme.labelSmall); + add('displayLargeEmphasized', displayLargeEmphasized, defaultTheme.displayLargeEmphasized); + add('displayMediumEmphasized', displayMediumEmphasized, defaultTheme.displayMediumEmphasized); + add('displaySmallEmphasized', displaySmallEmphasized, defaultTheme.displaySmallEmphasized); + add('headlineLargeEmphasized', headlineLargeEmphasized, defaultTheme.headlineLargeEmphasized); + add( + 'headlineMediumEmphasized', + headlineMediumEmphasized, + defaultTheme.headlineMediumEmphasized, + ); + add('headlineSmallEmphasized', headlineSmallEmphasized, defaultTheme.headlineSmallEmphasized); + add('titleLargeEmphasized', titleLargeEmphasized, defaultTheme.titleLargeEmphasized); + add('titleMediumEmphasized', titleMediumEmphasized, defaultTheme.titleMediumEmphasized); + add('titleSmallEmphasized', titleSmallEmphasized, defaultTheme.titleSmallEmphasized); + add('bodyLargeEmphasized', bodyLargeEmphasized, defaultTheme.bodyLargeEmphasized); + add('bodyMediumEmphasized', bodyMediumEmphasized, defaultTheme.bodyMediumEmphasized); + add('bodySmallEmphasized', bodySmallEmphasized, defaultTheme.bodySmallEmphasized); + add('labelLargeEmphasized', labelLargeEmphasized, defaultTheme.labelLargeEmphasized); + add('labelMediumEmphasized', labelMediumEmphasized, defaultTheme.labelMediumEmphasized); + add('labelSmallEmphasized', labelSmallEmphasized, defaultTheme.labelSmallEmphasized); } } diff --git a/packages/material_ui/lib/src/typography.dart b/packages/material_ui/lib/src/typography.dart index aef5de04708d..5aba798de8d2 100644 --- a/packages/material_ui/lib/src/typography.dart +++ b/packages/material_ui/lib/src/typography.dart @@ -15,6 +15,7 @@ import 'text_theme.dart'; import 'theme_data.dart'; part 'generated/typography_defaults_m3.g.dart'; +part 'generated/typography_defaults_m3e.g.dart'; // Examples can assume: // late TargetPlatform platform; @@ -70,17 +71,24 @@ enum ScriptCategory { /// font](https://developer.apple.com/design/human-interface-guidelines/typography/) /// fonts as used by Apple on iOS. /// -/// Three sets of geometry themes are provided: 2014, 2018, and 2021. The 2014 -/// themes correspond to the original version of the Material Design spec. The -/// 2018 themes correspond to the second iteration of the specification and -/// feature different font sizes, font weights, and letter spacing values. The -/// 2021 themes correspond to the Material Design 3 specification. +/// Four sets of geometry themes are provided: 2014, 2018, 2021, and 2025: /// -/// By default, [ThemeData.typography] is `Typography.material2021(platform: +/// * The 2014 themes correspond to the original version of the Material Design +/// spec. +/// * The 2018 themes correspond to the second iteration of the specification +/// and feature different font sizes, font weights, and letter spacing +/// values. +/// * The 2021 themes correspond to the Material Design 3 specification. +/// * The 2025 themes correspond to the Material Design 3 Expressive +/// specification and include emphasized variants for each of the 2021 text +/// styles. +/// +/// By default, [ThemeData.typography] is `Typography.material2025(platform: /// platform, colorScheme: colorScheme)` (or `Typography.material2014(platform: -/// platform)` when [ThemeData.useMaterial3] is false). To use the 2018 text -/// theme geometries, specify a value using the [Typography.material2018] -/// constructor: +/// platform)` when [ThemeData.useMaterial3] is false). To use a different set +/// of text theme geometries, specify a value using one of the [Typography] +/// constructors, such as [Typography.material2021] or +/// [Typography.material2018]: /// /// ```dart /// typography: Typography.material2018(platform: platform) @@ -225,18 +233,75 @@ class Typography with Diagnosticable { tall ?? tall2021, ); - // Ensure they are all uniformly dark or light, with - // no color variation based on style as it was in previous - // versions of Material Design. - final Color dark = colorScheme.brightness == Brightness.light - ? colorScheme.onSurface - : colorScheme.surface; - final Color light = colorScheme.brightness == Brightness.light - ? colorScheme.surface - : colorScheme.onSurface; - return base.copyWith( - black: base.black.apply(displayColor: dark, bodyColor: dark, decorationColor: dark), - white: base.white.apply(displayColor: light, bodyColor: light, decorationColor: light), + final (TextTheme coloredBlack, TextTheme coloredWhite) = _applyM3Colors( + base.black, + base.white, + colorScheme, + ); + return base.copyWith(black: coloredBlack, white: coloredWhite); + } + + /// Creates a typography instance using Material 3 Expressive (2025) + /// defaults. + /// + /// In addition to the fifteen baseline text styles, the resulting text + /// themes include the emphasized type styles (e.g. + /// [TextTheme.titleMediumEmphasized]). + /// + /// The [typeface] provides the font family, fallback fonts, and text + /// decoration for each style, but neither color nor geometry. Each + /// emphasized style in [typeface] that is null defaults to its baseline + /// style, so that emphasized text uses the same font family as its baseline + /// counterpart. + /// + /// If [typeface] is null, it defaults to the [platform]'s typeface: + /// [cupertino] on iOS, [redwoodCity] on macOS, [redmond] on Windows, + /// [helsinki] on Linux, and [mountainView] otherwise. If [platform] is null + /// then [typeface] must be specified. + /// + /// Text colors are derived from [colorScheme]. For a light [colorScheme], + /// every style in the resulting [Typography.black] uses + /// [ColorScheme.onSurface] and every style in [Typography.white] uses + /// [ColorScheme.surface]. For a dark [colorScheme], these are reversed. The + /// same colors are used for [TextStyle.decorationColor]. Any colors in + /// [typeface] are replaced. + /// + /// The default values for [englishLike], [dense], and [tall] are + /// [englishLike2025], [dense2025], and [tall2025]. + /// + /// See also: + /// * + factory Typography.material2025({ + TargetPlatform? platform = TargetPlatform.android, + ColorScheme colorScheme = const ColorScheme.light(), + TextTheme? typeface, + TextTheme? englishLike, + TextTheme? dense, + TextTheme? tall, + }) { + assert(platform != null || typeface != null); + final TextTheme resolvedTypeface = _withEmphasizedFromBaseline( + typeface ?? + switch (platform!) { + TargetPlatform.iOS => cupertino, + TargetPlatform.android || TargetPlatform.fuchsia => mountainView, + TargetPlatform.windows => redmond, + TargetPlatform.macOS => redwoodCity, + TargetPlatform.linux => helsinki, + }, + ); + + final (TextTheme black, TextTheme white) = _applyM3Colors( + resolvedTypeface, + resolvedTypeface, + colorScheme, + ); + return Typography._( + black, + white, + englishLike ?? englishLike2025, + dense ?? dense2025, + tall ?? tall2025, ); } @@ -263,6 +328,74 @@ class Typography with Diagnosticable { const Typography._(this.black, this.white, this.englishLike, this.dense, this.tall); + /// Returns [black] and [white] uniformly colored based on [colorScheme], as + /// specified by Material 3 onwards. + static (TextTheme, TextTheme) _applyM3Colors( + TextTheme black, + TextTheme white, + ColorScheme colorScheme, + ) { + // Ensure they are all uniformly dark or light, with + // no color variation based on style as it was in previous + // versions of Material Design. + final Color dark = colorScheme.brightness == Brightness.light + ? colorScheme.onSurface + : colorScheme.surface; + final Color light = colorScheme.brightness == Brightness.light + ? colorScheme.surface + : colorScheme.onSurface; + return ( + black.apply(displayColor: dark, bodyColor: dark, decorationColor: dark), + white.apply(displayColor: light, bodyColor: light, decorationColor: light), + ); + } + + /// Returns a copy of [textTheme] where each null emphasized style is + /// replaced by its baseline style. + /// + /// The platform typeface text themes (e.g. [mountainView]) only define + /// baseline styles, so this ensures emphasized styles inherit the same font + /// family and other non-geometric properties. + static TextTheme _withEmphasizedFromBaseline(TextTheme textTheme) { + TextStyle? emphasize(TextStyle? emphasized, TextStyle? baseline) { + if (emphasized != null || baseline == null) { + return emphasized; + } + final String? debugLabel = baseline.debugLabel; + return baseline.copyWith(debugLabel: debugLabel == null ? null : '${debugLabel}Emphasized'); + } + + return textTheme.copyWith( + displayLargeEmphasized: emphasize(textTheme.displayLargeEmphasized, textTheme.displayLarge), + displayMediumEmphasized: emphasize( + textTheme.displayMediumEmphasized, + textTheme.displayMedium, + ), + displaySmallEmphasized: emphasize(textTheme.displaySmallEmphasized, textTheme.displaySmall), + headlineLargeEmphasized: emphasize( + textTheme.headlineLargeEmphasized, + textTheme.headlineLarge, + ), + headlineMediumEmphasized: emphasize( + textTheme.headlineMediumEmphasized, + textTheme.headlineMedium, + ), + headlineSmallEmphasized: emphasize( + textTheme.headlineSmallEmphasized, + textTheme.headlineSmall, + ), + titleLargeEmphasized: emphasize(textTheme.titleLargeEmphasized, textTheme.titleLarge), + titleMediumEmphasized: emphasize(textTheme.titleMediumEmphasized, textTheme.titleMedium), + titleSmallEmphasized: emphasize(textTheme.titleSmallEmphasized, textTheme.titleSmall), + bodyLargeEmphasized: emphasize(textTheme.bodyLargeEmphasized, textTheme.bodyLarge), + bodyMediumEmphasized: emphasize(textTheme.bodyMediumEmphasized, textTheme.bodyMedium), + bodySmallEmphasized: emphasize(textTheme.bodySmallEmphasized, textTheme.bodySmall), + labelLargeEmphasized: emphasize(textTheme.labelLargeEmphasized, textTheme.labelLarge), + labelMediumEmphasized: emphasize(textTheme.labelMediumEmphasized, textTheme.labelMedium), + labelSmallEmphasized: emphasize(textTheme.labelSmallEmphasized, textTheme.labelSmall), + ); + } + /// A Material Design text theme with dark glyphs. /// /// This [TextTheme] should provide color but not geometry (font size, @@ -1413,6 +1546,434 @@ class Typography with Diagnosticable { ), ); + /// A Material Design text theme based on Roboto. + /// + /// This [TextTheme] provides the font family and text decoration for + /// each style, but neither color nor geometry (font size, weight, etc). + static const TextTheme mountainView = TextTheme( + displayLarge: TextStyle( + debugLabel: 'mountainView displayLarge', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + displayMedium: TextStyle( + debugLabel: 'mountainView displayMedium', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + displaySmall: TextStyle( + debugLabel: 'mountainView displaySmall', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + headlineLarge: TextStyle( + debugLabel: 'mountainView headlineLarge', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + headlineMedium: TextStyle( + debugLabel: 'mountainView headlineMedium', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + headlineSmall: TextStyle( + debugLabel: 'mountainView headlineSmall', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + titleLarge: TextStyle( + debugLabel: 'mountainView titleLarge', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + titleMedium: TextStyle( + debugLabel: 'mountainView titleMedium', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + titleSmall: TextStyle( + debugLabel: 'mountainView titleSmall', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + bodyLarge: TextStyle( + debugLabel: 'mountainView bodyLarge', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + bodyMedium: TextStyle( + debugLabel: 'mountainView bodyMedium', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + bodySmall: TextStyle( + debugLabel: 'mountainView bodySmall', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + labelLarge: TextStyle( + debugLabel: 'mountainView labelLarge', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + labelMedium: TextStyle( + debugLabel: 'mountainView labelMedium', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + labelSmall: TextStyle( + debugLabel: 'mountainView labelSmall', + fontFamily: 'Roboto', + decoration: TextDecoration.none, + ), + ); + + /// A Material Design text theme based on Segoe UI. + /// + /// This [TextTheme] provides the font family and text decoration for + /// each style, but neither color nor geometry (font size, weight, etc). + static const TextTheme redmond = TextTheme( + displayLarge: TextStyle( + debugLabel: 'redmond displayLarge', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + displayMedium: TextStyle( + debugLabel: 'redmond displayMedium', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + displaySmall: TextStyle( + debugLabel: 'redmond displaySmall', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + headlineLarge: TextStyle( + debugLabel: 'redmond headlineLarge', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + headlineMedium: TextStyle( + debugLabel: 'redmond headlineMedium', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + headlineSmall: TextStyle( + debugLabel: 'redmond headlineSmall', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + titleLarge: TextStyle( + debugLabel: 'redmond titleLarge', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + titleMedium: TextStyle( + debugLabel: 'redmond titleMedium', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + titleSmall: TextStyle( + debugLabel: 'redmond titleSmall', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + bodyLarge: TextStyle( + debugLabel: 'redmond bodyLarge', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + bodyMedium: TextStyle( + debugLabel: 'redmond bodyMedium', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + bodySmall: TextStyle( + debugLabel: 'redmond bodySmall', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + labelLarge: TextStyle( + debugLabel: 'redmond labelLarge', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + labelMedium: TextStyle( + debugLabel: 'redmond labelMedium', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + labelSmall: TextStyle( + debugLabel: 'redmond labelSmall', + fontFamily: 'Segoe UI', + decoration: TextDecoration.none, + ), + ); + + /// A Material Design text theme based on Roboto, with fallback fonts that are likely + /// (but not guaranteed) to be installed on Linux. + /// + /// This [TextTheme] provides the font family and text decoration for + /// each style, but neither color nor geometry (font size, weight, etc). + static const TextTheme helsinki = TextTheme( + displayLarge: TextStyle( + debugLabel: 'helsinki displayLarge', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + displayMedium: TextStyle( + debugLabel: 'helsinki displayMedium', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + displaySmall: TextStyle( + debugLabel: 'helsinki displaySmall', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + headlineLarge: TextStyle( + debugLabel: 'helsinki headlineLarge', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + headlineMedium: TextStyle( + debugLabel: 'helsinki headlineMedium', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + headlineSmall: TextStyle( + debugLabel: 'helsinki headlineSmall', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + titleLarge: TextStyle( + debugLabel: 'helsinki titleLarge', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + titleMedium: TextStyle( + debugLabel: 'helsinki titleMedium', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + titleSmall: TextStyle( + debugLabel: 'helsinki titleSmall', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + bodyLarge: TextStyle( + debugLabel: 'helsinki bodyLarge', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + bodyMedium: TextStyle( + debugLabel: 'helsinki bodyMedium', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + bodySmall: TextStyle( + debugLabel: 'helsinki bodySmall', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + labelLarge: TextStyle( + debugLabel: 'helsinki labelLarge', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + labelMedium: TextStyle( + debugLabel: 'helsinki labelMedium', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + labelSmall: TextStyle( + debugLabel: 'helsinki labelSmall', + fontFamily: 'Roboto', + fontFamilyFallback: _helsinkiFontFallbacks, + decoration: TextDecoration.none, + ), + ); + + /// A Material Design text theme based on San Francisco. + /// + /// This [TextTheme] provides the font family and text decoration for + /// each style, but neither color nor geometry (font size, weight, etc). + /// + /// This theme uses the iOS version of the font names. + static const TextTheme cupertino = TextTheme( + displayLarge: TextStyle( + debugLabel: 'cupertino displayLarge', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + displayMedium: TextStyle( + debugLabel: 'cupertino displayMedium', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + displaySmall: TextStyle( + debugLabel: 'cupertino displaySmall', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + headlineLarge: TextStyle( + debugLabel: 'cupertino headlineLarge', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + headlineMedium: TextStyle( + debugLabel: 'cupertino headlineMedium', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + headlineSmall: TextStyle( + debugLabel: 'cupertino headlineSmall', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + titleLarge: TextStyle( + debugLabel: 'cupertino titleLarge', + fontFamily: 'CupertinoSystemDisplay', + decoration: TextDecoration.none, + ), + titleMedium: TextStyle( + debugLabel: 'cupertino titleMedium', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + titleSmall: TextStyle( + debugLabel: 'cupertino titleSmall', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + bodyLarge: TextStyle( + debugLabel: 'cupertino bodyLarge', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + bodyMedium: TextStyle( + debugLabel: 'cupertino bodyMedium', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + bodySmall: TextStyle( + debugLabel: 'cupertino bodySmall', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + labelLarge: TextStyle( + debugLabel: 'cupertino labelLarge', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + labelMedium: TextStyle( + debugLabel: 'cupertino labelMedium', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + labelSmall: TextStyle( + debugLabel: 'cupertino labelSmall', + fontFamily: 'CupertinoSystemText', + decoration: TextDecoration.none, + ), + ); + + /// A Material Design text theme based on San Francisco. + /// + /// This [TextTheme] provides the font family and text decoration for + /// each style, but neither color nor geometry (font size, weight, etc). + static const TextTheme redwoodCity = TextTheme( + displayLarge: TextStyle( + debugLabel: 'redwoodCity displayLarge', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + displayMedium: TextStyle( + debugLabel: 'redwoodCity displayMedium', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + displaySmall: TextStyle( + debugLabel: 'redwoodCity displaySmall', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + headlineLarge: TextStyle( + debugLabel: 'redwoodCity headlineLarge', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + headlineMedium: TextStyle( + debugLabel: 'redwoodCity headlineMedium', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + headlineSmall: TextStyle( + debugLabel: 'redwoodCity headlineSmall', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + titleLarge: TextStyle( + debugLabel: 'redwoodCity titleLarge', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + titleMedium: TextStyle( + debugLabel: 'redwoodCity titleMedium', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + titleSmall: TextStyle( + debugLabel: 'redwoodCity titleSmall', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + bodyLarge: TextStyle( + debugLabel: 'redwoodCity bodyLarge', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + bodyMedium: TextStyle( + debugLabel: 'redwoodCity bodyMedium', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + bodySmall: TextStyle( + debugLabel: 'redwoodCity bodySmall', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + labelLarge: TextStyle( + debugLabel: 'redwoodCity labelLarge', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + labelMedium: TextStyle( + debugLabel: 'redwoodCity labelMedium', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + labelSmall: TextStyle( + debugLabel: 'redwoodCity labelSmall', + fontFamily: '.AppleSystemUIFont', + decoration: TextDecoration.none, + ), + ); + /// Defines text geometry for `ScriptCategory.englishLike` scripts, such as /// English, French, Russian, etc. static const TextTheme englishLike2014 = TextTheme( @@ -2116,4 +2677,26 @@ class Typography with Diagnosticable { /// The Material Design 3 specification does not include 'tall' text themes, /// so this is just here to be consistent with the API. static const TextTheme tall2021 = _M3Typography.tall; + + /// Defines text geometry for `ScriptCategory.englishLike` scripts, such as + /// English, French, Russian, etc. + /// + /// The font sizes, weights, and letter spacings in this version match the + /// [Material 3 Expressive specification](https://m3.material.io/styles/typography/overview), + /// and include the emphasized type styles (e.g. + /// [TextTheme.titleMediumEmphasized]). + static const TextTheme englishLike2025 = _M3ETypography.englishLike; + + /// Defines text geometry for dense scripts, such as Chinese, Japanese + /// and Korean. + /// + /// The Material 3 Expressive specification does not include 'dense' text + /// themes, so this is just here to be consistent with the API. + static const TextTheme dense2025 = _M3ETypography.dense; + + /// Defines text geometry for tall scripts, such as Farsi, Hindi, and Thai. + /// + /// The Material 3 Expressive specification does not include 'tall' text + /// themes, so this is just here to be consistent with the API. + static const TextTheme tall2025 = _M3ETypography.tall; } From 1b7278af78c5eb3e44902782d41730207974e1fe Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 13:32:54 -0700 Subject: [PATCH 03/11] Use material2025 when useMaterial3 is true --- packages/material_ui/lib/src/theme_data.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material_ui/lib/src/theme_data.dart b/packages/material_ui/lib/src/theme_data.dart index 3669b964de57..b9f9cbb3735d 100644 --- a/packages/material_ui/lib/src/theme_data.dart +++ b/packages/material_ui/lib/src/theme_data.dart @@ -597,7 +597,7 @@ class ThemeData with Diagnosticable { // TYPOGRAPHY & ICONOGRAPHY typography ??= useMaterial3 - ? Typography.material2021(platform: platform, colorScheme: colorScheme) + ? Typography.material2025(platform: platform, colorScheme: colorScheme) : Typography.material2014(platform: platform); TextTheme defaultTextTheme = isDark ? typography.white : typography.black; TextTheme defaultPrimaryTextTheme = primaryIsDark ? typography.white : typography.black; From c062f8c4be8fb267baa89d3459a181f447cd8f85 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 14:36:47 -0700 Subject: [PATCH 04/11] Add and update tests --- .../material_ui/test/text_theme_test.dart | 192 ++++++++++++++ .../material_ui/test/theme_data_test.dart | 6 +- packages/material_ui/test/theme_test.dart | 8 +- packages/material_ui/test/tooltip_test.dart | 6 +- .../material_ui/test/typography_test.dart | 244 ++++++++++++++++++ 5 files changed, 446 insertions(+), 10 deletions(-) diff --git a/packages/material_ui/test/text_theme_test.dart b/packages/material_ui/test/text_theme_test.dart index 3710d05254b6..a5aa99e581e8 100644 --- a/packages/material_ui/test/text_theme_test.dart +++ b/packages/material_ui/test/text_theme_test.dart @@ -566,4 +566,196 @@ void main() { expect(nonDefaultPropertyNames, ['bodyMedium']); }); + + group('TextTheme emphasized styles', () { + // A text theme where every emphasized style is distinct from its baseline + // style and from every other style. + TextTheme distinctTheme({double offset = 0.0}) { + var size = offset; + TextStyle next() => TextStyle(fontSize: ++size); + return TextTheme( + displayLarge: next(), + displayMedium: next(), + displaySmall: next(), + headlineLarge: next(), + headlineMedium: next(), + headlineSmall: next(), + titleLarge: next(), + titleMedium: next(), + titleSmall: next(), + bodyLarge: next(), + bodyMedium: next(), + bodySmall: next(), + labelLarge: next(), + labelMedium: next(), + labelSmall: next(), + displayLargeEmphasized: next(), + displayMediumEmphasized: next(), + displaySmallEmphasized: next(), + headlineLargeEmphasized: next(), + headlineMediumEmphasized: next(), + headlineSmallEmphasized: next(), + titleLargeEmphasized: next(), + titleMediumEmphasized: next(), + titleSmallEmphasized: next(), + bodyLargeEmphasized: next(), + bodyMediumEmphasized: next(), + bodySmallEmphasized: next(), + labelLargeEmphasized: next(), + labelMediumEmphasized: next(), + labelSmallEmphasized: next(), + ); + } + + test('default to null', () { + expect(_emphasizedStyles(const TextTheme()), everyElement(isNull)); + expect(_emphasizedStyles(Typography.material2021().black), everyElement(isNull)); + }); + + test('copyWith replaces each emphasized style', () { + final TextTheme source = distinctTheme(offset: 100.0); + final TextTheme copy = const TextTheme().copyWith( + displayLargeEmphasized: source.displayLargeEmphasized, + displayMediumEmphasized: source.displayMediumEmphasized, + displaySmallEmphasized: source.displaySmallEmphasized, + headlineLargeEmphasized: source.headlineLargeEmphasized, + headlineMediumEmphasized: source.headlineMediumEmphasized, + headlineSmallEmphasized: source.headlineSmallEmphasized, + titleLargeEmphasized: source.titleLargeEmphasized, + titleMediumEmphasized: source.titleMediumEmphasized, + titleSmallEmphasized: source.titleSmallEmphasized, + bodyLargeEmphasized: source.bodyLargeEmphasized, + bodyMediumEmphasized: source.bodyMediumEmphasized, + bodySmallEmphasized: source.bodySmallEmphasized, + labelLargeEmphasized: source.labelLargeEmphasized, + labelMediumEmphasized: source.labelMediumEmphasized, + labelSmallEmphasized: source.labelSmallEmphasized, + ); + expect(_emphasizedStyles(copy), _emphasizedStyles(source)); + expect(_baselineStyles(copy), everyElement(isNull)); + + // Emphasized styles are preserved when not overridden. + expect(source.copyWith(), source); + }); + + test('merge merges each emphasized style', () { + final TextTheme base = distinctTheme(); + const color = Color(0xFF123456); + const other = TextTheme( + titleMediumEmphasized: TextStyle(color: color), + labelSmallEmphasized: TextStyle(color: color), + ); + final TextTheme merged = base.merge(other); + expect(merged.titleMediumEmphasized, base.titleMediumEmphasized!.copyWith(color: color)); + expect(merged.labelSmallEmphasized, base.labelSmallEmphasized!.copyWith(color: color)); + expect(merged.bodyMediumEmphasized, base.bodyMediumEmphasized); + + // Null emphasized styles take the other theme's style. + final TextTheme fromEmpty = const TextTheme().merge(base); + expect(_emphasizedStyles(fromEmpty), _emphasizedStyles(base)); + }); + + test('apply uses the same color as the baseline style', () { + const displayColor = Color(0x00000001); + const bodyColor = Color(0x00000002); + final TextTheme applied = distinctTheme().apply( + displayColor: displayColor, + bodyColor: bodyColor, + fontFamily: 'fontFamily', + fontSizeFactor: 2.0, + ); + + final List baseline = _baselineStyles(applied); + final List emphasized = _emphasizedStyles(applied); + for (var i = 0; i < baseline.length; i++) { + expect(emphasized[i]!.color, baseline[i]!.color); + expect(emphasized[i]!.fontFamily, 'fontFamily'); + } + expect(applied.displayLargeEmphasized!.color, displayColor); + expect(applied.bodySmallEmphasized!.color, displayColor); + expect(applied.bodyLargeEmphasized!.color, bodyColor); + expect(applied.labelSmallEmphasized!.color, bodyColor); + expect(applied.displayLargeEmphasized!.fontSize, 32.0); + + // Null emphasized styles stay null. + expect( + _emphasizedStyles(const TextTheme().apply(bodyColor: bodyColor)), + everyElement(isNull), + ); + }); + + test('lerp interpolates each emphasized style', () { + final TextTheme a = distinctTheme(); + final TextTheme b = distinctTheme(offset: 100.0); + final TextTheme lerped = TextTheme.lerp(a, b, 0.25); + + final List aStyles = _emphasizedStyles(a); + final List bStyles = _emphasizedStyles(b); + final List lerpedStyles = _emphasizedStyles(lerped); + for (var i = 0; i < lerpedStyles.length; i++) { + expect(lerpedStyles[i], TextStyle.lerp(aStyles[i], bStyles[i], 0.25)); + } + }); + + test('equality and hashCode account for emphasized styles', () { + final TextTheme theme = distinctTheme(); + expect(theme, distinctTheme()); + expect(theme.hashCode, distinctTheme().hashCode); + + final TextTheme changed = theme.copyWith( + labelSmallEmphasized: theme.labelSmallEmphasized!.copyWith(color: const Color(0xFF123456)), + ); + expect(changed, isNot(theme)); + expect(changed.hashCode, isNot(theme.hashCode)); + }); + + test('debugFillProperties reports emphasized styles', () { + final builder = DiagnosticPropertiesBuilder(); + const TextTheme(titleMediumEmphasized: TextStyle(fontSize: 16.0)) + .debugFillProperties(builder); + + final List nonDefaultPropertyNames = builder.properties + .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) + .map((DiagnosticsNode node) => node.name!) + .toList(); + + expect(nonDefaultPropertyNames, ['titleMediumEmphasized']); + }); + }); } + +List _baselineStyles(TextTheme theme) => [ + theme.displayLarge, + theme.displayMedium, + theme.displaySmall, + theme.headlineLarge, + theme.headlineMedium, + theme.headlineSmall, + theme.titleLarge, + theme.titleMedium, + theme.titleSmall, + theme.bodyLarge, + theme.bodyMedium, + theme.bodySmall, + theme.labelLarge, + theme.labelMedium, + theme.labelSmall, +]; + +List _emphasizedStyles(TextTheme theme) => [ + theme.displayLargeEmphasized, + theme.displayMediumEmphasized, + theme.displaySmallEmphasized, + theme.headlineLargeEmphasized, + theme.headlineMediumEmphasized, + theme.headlineSmallEmphasized, + theme.titleLargeEmphasized, + theme.titleMediumEmphasized, + theme.titleSmallEmphasized, + theme.bodyLargeEmphasized, + theme.bodyMediumEmphasized, + theme.bodySmallEmphasized, + theme.labelLargeEmphasized, + theme.labelMediumEmphasized, + theme.labelSmallEmphasized, +]; diff --git a/packages/material_ui/test/theme_data_test.dart b/packages/material_ui/test/theme_data_test.dart index 2e205bfa9aa9..1922bdac6488 100644 --- a/packages/material_ui/test/theme_data_test.dart +++ b/packages/material_ui/test/theme_data_test.dart @@ -113,17 +113,17 @@ void main() { test('light, dark and fallback constructors support useMaterial3', () { final lightTheme = ThemeData(); expect(lightTheme.useMaterial3, true); - expect(lightTheme.typography, Typography.material2021(colorScheme: lightTheme.colorScheme)); + expect(lightTheme.typography, Typography.material2025(colorScheme: lightTheme.colorScheme)); final darkTheme = ThemeData.dark(); expect(darkTheme.useMaterial3, true); - expect(darkTheme.typography, Typography.material2021(colorScheme: darkTheme.colorScheme)); + expect(darkTheme.typography, Typography.material2025(colorScheme: darkTheme.colorScheme)); final fallbackTheme = ThemeData(); expect(fallbackTheme.useMaterial3, true); expect( fallbackTheme.typography, - Typography.material2021(colorScheme: fallbackTheme.colorScheme), + Typography.material2025(colorScheme: fallbackTheme.colorScheme), ); }); diff --git a/packages/material_ui/test/theme_test.dart b/packages/material_ui/test/theme_test.dart index 741ce7fc34f8..e7b61a43601f 100644 --- a/packages/material_ui/test/theme_test.dart +++ b/packages/material_ui/test/theme_test.dart @@ -10,7 +10,7 @@ import 'package:material_ui/material_ui.dart'; void main() { const TextTheme defaultGeometryTheme = Typography.englishLike2014; - const TextTheme defaultGeometryThemeM3 = Typography.englishLike2021; + const TextTheme defaultGeometryThemeM3 = Typography.englishLike2025; test('ThemeDataTween control test', () { final light = ThemeData(); @@ -273,11 +273,11 @@ void main() { expect(m2Theme.typography, Typography.material2014()); }); - testWidgets('Material3 - ThemeData with null typography uses proper defaults', ( + testWidgets('Material3 - ThemeData with null typography uses 2025 defaults', ( WidgetTester tester, ) async { final m3Theme = ThemeData(); - expect(m3Theme.typography, Typography.material2021(colorScheme: m3Theme.colorScheme)); + expect(m3Theme.typography, Typography.material2025(colorScheme: m3Theme.colorScheme)); }); testWidgets('PopupMenu inherits shadowed app theme', (WidgetTester tester) async { @@ -670,7 +670,7 @@ void main() { expect( theme.textTheme.displayLarge!.debugLabel, - '(englishLike displayLarge 2021).merge((blackMountainView displayLarge).apply)', + '(englishLike displayLarge 2025).merge((mountainView displayLarge).apply)', ); }); diff --git a/packages/material_ui/test/tooltip_test.dart b/packages/material_ui/test/tooltip_test.dart index 246b4420adba..202dd355ab0e 100644 --- a/packages/material_ui/test/tooltip_test.dart +++ b/packages/material_ui/test/tooltip_test.dart @@ -839,7 +839,7 @@ void main() { expect(textStyle.decoration, TextDecoration.none); expect( textStyle.debugLabel, - '((englishLike bodyMedium 2021).merge((blackMountainView bodyMedium).apply)).copyWith', + '((englishLike bodyMedium 2025).merge((mountainView bodyMedium).apply)).copyWith', ); }); @@ -889,7 +889,7 @@ void main() { expect(textStyle.decoration, TextDecoration.none); expect( textStyle.debugLabel, - '((englishLike bodyMedium 2021).merge((whiteMountainView bodyMedium).apply)).copyWith', + '((englishLike bodyMedium 2025).merge((mountainView bodyMedium).apply)).copyWith', ); }); @@ -1802,7 +1802,7 @@ void main() { expect(textStyle.style.decoration, TextDecoration.none); expect( textStyle.style.debugLabel, - '((englishLike bodyMedium 2021).merge((blackMountainView bodyMedium).apply)).copyWith', + '((englishLike bodyMedium 2025).merge((mountainView bodyMedium).apply)).copyWith', ); }); diff --git a/packages/material_ui/test/typography_test.dart b/packages/material_ui/test/typography_test.dart index 4e87b38ea05b..9e846ac827f1 100644 --- a/packages/material_ui/test/typography_test.dart +++ b/packages/material_ui/test/typography_test.dart @@ -538,4 +538,248 @@ void main() { expect(defaults.dense, Typography.dense2021); expect(defaults.tall, Typography.tall2021); }); + + group('Typography.material2025', () { + const lightScheme = ColorScheme.light(onSurface: Color(0xFF000001), surface: Color(0xFFFFFFF1)); + const darkScheme = ColorScheme.dark(onSurface: Color(0xFFFFFFF2), surface: Color(0xFF000002)); + + for (final TargetPlatform platform in TargetPlatform.values) { + for (final colorScheme in [lightScheme, darkScheme]) { + group('on $platform with ${colorScheme.brightness}', () { + // Each pair of corresponding material2021 and material2025 text themes. + List<(String, TextTheme, TextTheme)> textThemePairs() { + final m3 = Typography.material2021(platform: platform, colorScheme: colorScheme); + final m3e = Typography.material2025(platform: platform, colorScheme: colorScheme); + return <(String, TextTheme, TextTheme)>[ + ('black', m3.black, m3e.black), + ('white', m3.white, m3e.white), + ('englishLike', m3.englishLike, m3e.englishLike), + ('dense', m3.dense, m3e.dense), + ('tall', m3.tall, m3e.tall), + ]; + } + + test('matches material2021 baseline styles', () { + for (final (String name, TextTheme m3Theme, TextTheme m3eTheme) in textThemePairs()) { + final List m3Baseline = _baselineStyles(m3Theme); + final List m3eBaseline = _baselineStyles(m3eTheme); + for (var i = 0; i < m3Baseline.length; i++) { + expect(m3Baseline[i], isNotNull, reason: name); + // TextStyle equality ignores debugLabel, which differs between + // the two versions. + expect(m3eBaseline[i], m3Baseline[i], reason: name); + } + } + }); + + test('adds emphasized styles that material2021 does not have', () { + for (final (String name, TextTheme m3Theme, TextTheme m3eTheme) in textThemePairs()) { + expect(_emphasizedStyles(m3Theme), everyElement(isNull), reason: name); + expect(_emphasizedStyles(m3eTheme), everyElement(isNotNull), reason: name); + } + }); + }); + } + } + + test('emphasized styles inherit the font of their baseline style', () { + for (final TargetPlatform platform in TargetPlatform.values) { + final typography = Typography.material2025(platform: platform); + for (final theme in [typography.black, typography.white]) { + final List baseline = _baselineStyles(theme); + final List emphasized = _emphasizedStyles(theme); + for (var i = 0; i < baseline.length; i++) { + expect(emphasized[i], baseline[i], reason: '$platform'); + } + } + } + }); + + test('emphasized geometry matches the baseline geometry with a heavier weight', () { + for (final theme in [ + Typography.englishLike2025, + Typography.dense2025, + Typography.tall2025, + ]) { + final List baseline = _baselineStyles(theme); + final List emphasized = _emphasizedStyles(theme); + for (var i = 0; i < baseline.length; i++) { + final TextStyle base = baseline[i]!; + final TextStyle emph = emphasized[i]!; + expect(emph.fontSize, base.fontSize, reason: emph.debugLabel); + expect(emph.height, base.height, reason: emph.debugLabel); + expect(emph.letterSpacing, base.letterSpacing, reason: emph.debugLabel); + expect(emph.textBaseline, base.textBaseline, reason: emph.debugLabel); + expect(emph.leadingDistribution, base.leadingDistribution, reason: emph.debugLabel); + expect( + emph.fontWeight!.value, + greaterThan(base.fontWeight!.value), + reason: emph.debugLabel, + ); + } + } + }); + + test('englishLike2025 emphasized styles match Material 3 Expressive spec', () { + const TextTheme theme = Typography.englishLike2025; + expect(theme.displayLargeEmphasized!.fontWeight, FontWeight.w500); + expect(theme.headlineMediumEmphasized!.fontWeight, FontWeight.w500); + expect(theme.titleLargeEmphasized!.fontWeight, FontWeight.w500); + expect(theme.titleMediumEmphasized!.fontWeight, FontWeight.w700); + expect(theme.titleSmallEmphasized!.fontWeight, FontWeight.w700); + expect(theme.bodyLargeEmphasized!.fontWeight, FontWeight.w500); + expect(theme.labelLargeEmphasized!.fontWeight, FontWeight.w700); + expect(theme.labelSmallEmphasized!.fontSize, 11.0); + expect(theme.labelSmallEmphasized!.height, 1.45); + }); + + test('colors black and white text themes from the colorScheme', () { + void expectUniform(TextTheme theme, Color color) { + for (final style in [..._baselineStyles(theme), ..._emphasizedStyles(theme)]) { + expect(style!.color, color); + expect(style.decorationColor, color); + } + } + + final light = Typography.material2025(colorScheme: lightScheme); + expectUniform(light.black, lightScheme.onSurface); + expectUniform(light.white, lightScheme.surface); + + final dark = Typography.material2025(colorScheme: darkScheme); + expectUniform(dark.black, darkScheme.surface); + expectUniform(dark.white, darkScheme.onSurface); + }); + + test('defaults the typeface based on the platform', () { + final expected = { + TargetPlatform.android: Typography.mountainView, + TargetPlatform.fuchsia: Typography.mountainView, + TargetPlatform.iOS: Typography.cupertino, + TargetPlatform.linux: Typography.helsinki, + TargetPlatform.macOS: Typography.redwoodCity, + TargetPlatform.windows: Typography.redmond, + }; + for (final TargetPlatform platform in TargetPlatform.values) { + final typography = Typography.material2025(platform: platform); + final List actual = _baselineStyles(typography.black); + final List typeface = _baselineStyles(expected[platform]!); + for (var i = 0; i < actual.length; i++) { + expect(actual[i]!.fontFamily, typeface[i]!.fontFamily, reason: '$platform'); + expect( + actual[i]!.fontFamilyFallback, + typeface[i]!.fontFamilyFallback, + reason: '$platform', + ); + } + } + }); + + test('uses the provided typeface for non-color properties', () { + const typeface = TextTheme( + bodyMedium: TextStyle( + fontFamily: 'Custom', + color: Color(0xFFFF0000), + decorationColor: Color(0xFF00FF00), + ), + titleMediumEmphasized: TextStyle(fontFamily: 'CustomEmphasized'), + ); + // The typeface is used even when platform is null. + final typography = Typography.material2025( + platform: null, + colorScheme: lightScheme, + typeface: typeface, + ); + + expect(typography.black.bodyMedium!.fontFamily, 'Custom'); + expect(typography.black.bodyMedium!.color, lightScheme.onSurface); + expect(typography.black.bodyMedium!.decorationColor, lightScheme.onSurface); + expect(typography.white.bodyMedium!.fontFamily, 'Custom'); + expect(typography.white.bodyMedium!.color, lightScheme.surface); + expect(typography.white.bodyMedium!.decorationColor, lightScheme.surface); + + // A null emphasized style falls back to its baseline style. + expect(typography.black.bodyMediumEmphasized!.fontFamily, 'Custom'); + expect(typography.black.bodyMediumEmphasized!.color, lightScheme.onSurface); + // A provided emphasized style is kept. + expect(typography.black.titleMediumEmphasized!.fontFamily, 'CustomEmphasized'); + expect(typography.black.titleMediumEmphasized!.color, lightScheme.onSurface); + // Styles that are null in both the baseline and emphasized variants stay null. + expect(typography.black.bodyLarge, isNull); + expect(typography.black.bodyLargeEmphasized, isNull); + }); + + test('uses the provided geometry text themes', () { + const englishLike = TextTheme(bodyMedium: TextStyle(fontSize: 1.0)); + const dense = TextTheme(bodyMedium: TextStyle(fontSize: 2.0)); + const tall = TextTheme(bodyMedium: TextStyle(fontSize: 3.0)); + + final typography = Typography.material2025( + englishLike: englishLike, + dense: dense, + tall: tall, + ); + expect(typography.englishLike, englishLike); + expect(typography.dense, dense); + expect(typography.tall, tall); + + final defaults = Typography.material2025(); + expect(defaults.englishLike, Typography.englishLike2025); + expect(defaults.dense, Typography.dense2025); + expect(defaults.tall, Typography.tall2025); + }); + + test('is the default ThemeData typography and provides emphasized styles', () { + final theme = ThemeData(); + expect(theme.typography, Typography.material2025(colorScheme: theme.colorScheme)); + + // Geometry is merged into the text theme when it is localized. + final TextTheme textTheme = ThemeData.localize(theme, theme.typography.englishLike).textTheme; + for (final TextStyle? style in _emphasizedStyles(textTheme)) { + expect(style, isNotNull); + expect(style!.color, theme.colorScheme.onSurface); + expect(style.fontFamily, 'Roboto'); + } + expect(textTheme.titleMediumEmphasized!.fontSize, 16.0); + expect(textTheme.titleMediumEmphasized!.fontWeight, FontWeight.w700); + + final m3Theme = ThemeData(typography: Typography.material2021()); + expect(_emphasizedStyles(m3Theme.textTheme), everyElement(isNull)); + }); + }); } + +List _baselineStyles(TextTheme theme) => [ + theme.displayLarge, + theme.displayMedium, + theme.displaySmall, + theme.headlineLarge, + theme.headlineMedium, + theme.headlineSmall, + theme.titleLarge, + theme.titleMedium, + theme.titleSmall, + theme.bodyLarge, + theme.bodyMedium, + theme.bodySmall, + theme.labelLarge, + theme.labelMedium, + theme.labelSmall, +]; + +List _emphasizedStyles(TextTheme theme) => [ + theme.displayLargeEmphasized, + theme.displayMediumEmphasized, + theme.displaySmallEmphasized, + theme.headlineLargeEmphasized, + theme.headlineMediumEmphasized, + theme.headlineSmallEmphasized, + theme.titleLargeEmphasized, + theme.titleMediumEmphasized, + theme.titleSmallEmphasized, + theme.bodyLargeEmphasized, + theme.bodyMediumEmphasized, + theme.bodySmallEmphasized, + theme.labelLargeEmphasized, + theme.labelMediumEmphasized, + theme.labelSmallEmphasized, +]; From 2affc1fe2a47c0da922f1a92fd15dc12aa98b00e Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 14:39:42 -0700 Subject: [PATCH 05/11] Add CHANGELOG entry --- .../change_2026_10_28_typography_2025.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml diff --git a/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml b/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml new file mode 100644 index 000000000000..1f9569eafc66 --- /dev/null +++ b/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml @@ -0,0 +1,6 @@ +changelog: | + - Adds `Typography.material2025`, which provides Material 3 Expressive typography, along with the `Typography.englishLike2025`, `Typography.dense2025`, and `Typography.tall2025` geometry text themes. + - Adds emphasized text styles to `TextTheme` (e.g. `TextTheme.titleMediumEmphasized`). These are only populated by `Typography.material2025`. + - Adds the `Typography.mountainView`, `Typography.redmond`, `Typography.helsinki`, `Typography.cupertino`, and `Typography.redwoodCity` text themes, which provide each platform's typeface without color or geometry. + - Changes the default `ThemeData.typography` to `Typography.material2025` when `ThemeData.useMaterial3` is true. The baseline text styles are unchanged. +version: minor From 37bd116c6580c6c07e2262087c3ef696d75effa0 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:21:56 -0700 Subject: [PATCH 06/11] Clean up --- packages/material_ui/lib/src/theme_data.dart | 2 +- packages/material_ui/lib/src/typography.dart | 2 +- .../change_2026_10_28_typography_2025.yaml | 2 +- .../material_ui/test/typography_test.dart | 69 +++++++++++++++++++ 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/packages/material_ui/lib/src/theme_data.dart b/packages/material_ui/lib/src/theme_data.dart index b9f9cbb3735d..a92c925751b5 100644 --- a/packages/material_ui/lib/src/theme_data.dart +++ b/packages/material_ui/lib/src/theme_data.dart @@ -1188,7 +1188,7 @@ class ThemeData with Diagnosticable { /// | Property | Material 3 default | Material 2 default | /// | :-------------- | :----------------------------- | :----------------------------- | /// | [colorScheme] | M3 baseline light color scheme | M2 baseline light color scheme | - /// | [typography] | [Typography.material2021] | [Typography.material2014] | + /// | [typography] | [Typography.material2025] | [Typography.material2014] | /// | [splashFactory] | [InkSparkle]* or [InkRipple] | [InkSplash] | /// /// \* if the target platform is Android and the app is not diff --git a/packages/material_ui/lib/src/typography.dart b/packages/material_ui/lib/src/typography.dart index 5aba798de8d2..6356e0d1bb28 100644 --- a/packages/material_ui/lib/src/typography.dart +++ b/packages/material_ui/lib/src/typography.dart @@ -279,7 +279,7 @@ class Typography with Diagnosticable { TextTheme? dense, TextTheme? tall, }) { - assert(platform != null || typeface != null); + assert(platform != null || typeface != null, 'Either platform or typeface must be provided.'); final TextTheme resolvedTypeface = _withEmphasizedFromBaseline( typeface ?? switch (platform!) { diff --git a/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml b/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml index 1f9569eafc66..749571e4babb 100644 --- a/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml +++ b/packages/material_ui/pending_changelogs/change_2026_10_28_typography_2025.yaml @@ -2,5 +2,5 @@ changelog: | - Adds `Typography.material2025`, which provides Material 3 Expressive typography, along with the `Typography.englishLike2025`, `Typography.dense2025`, and `Typography.tall2025` geometry text themes. - Adds emphasized text styles to `TextTheme` (e.g. `TextTheme.titleMediumEmphasized`). These are only populated by `Typography.material2025`. - Adds the `Typography.mountainView`, `Typography.redmond`, `Typography.helsinki`, `Typography.cupertino`, and `Typography.redwoodCity` text themes, which provide each platform's typeface without color or geometry. - - Changes the default `ThemeData.typography` to `Typography.material2025` when `ThemeData.useMaterial3` is true. The baseline text styles are unchanged. + - Changes the default `ThemeData.typography` to `Typography.material2025` when `ThemeData.useMaterial3` is true. The baseline text styles have the same values as before, but the default text theme now also includes the emphasized styles, and its `TextStyle.debugLabel`s change (e.g. `englishLike bodyMedium 2025`). version: minor diff --git a/packages/material_ui/test/typography_test.dart b/packages/material_ui/test/typography_test.dart index 9e846ac827f1..b4b0df2c6cc4 100644 --- a/packages/material_ui/test/typography_test.dart +++ b/packages/material_ui/test/typography_test.dart @@ -745,6 +745,75 @@ void main() { final m3Theme = ThemeData(typography: Typography.material2021()); expect(_emphasizedStyles(m3Theme.textTheme), everyElement(isNull)); }); + + test('lerps to and from material2021', () { + final m3 = Typography.material2021(colorScheme: lightScheme); + final m3e = Typography.material2025(colorScheme: lightScheme); + + for (final t in [0.0, 0.25, 0.5, 0.75, 1.0]) { + for (final (Typography a, Typography b) in <(Typography, Typography)>[ + (m3, m3e), + (m3e, m3), + ]) { + final Typography lerped = Typography.lerp(a, b, t); + for (final (TextTheme lerpedTheme, TextTheme m3Theme) in <(TextTheme, TextTheme)>[ + (lerped.black, m3.black), + (lerped.white, m3.white), + (lerped.englishLike, m3.englishLike), + ]) { + // The baseline styles are identical, so they are unchanged by lerping. + expect(_baselineStyles(lerpedTheme), _baselineStyles(m3Theme), reason: 't = $t'); + } + } + } + + // Emphasized styles animate between null and the material2025 styles. + expect( + _emphasizedStyles(Typography.lerp(m3, m3e, 1.0).englishLike), + _emphasizedStyles(m3e.englishLike), + ); + expect( + _emphasizedStyles(Typography.lerp(m3e, m3, 0.0).englishLike), + _emphasizedStyles(m3e.englishLike), + ); + }); + + test('ThemeData lerps to and from material2021', () { + final m3Theme = ThemeData(typography: Typography.material2021()); + final m3eTheme = ThemeData(typography: Typography.material2025()); + + for (final t in [0.0, 0.25, 0.5, 0.75, 1.0]) { + final ThemeData lerped = ThemeData.lerp(m3Theme, m3eTheme, t); + expect( + _baselineStyles(lerped.textTheme), + _baselineStyles(m3Theme.textTheme), + reason: 't = $t', + ); + } + expect( + _emphasizedStyles(ThemeData.lerp(m3Theme, m3eTheme, 1.0).textTheme), + _emphasizedStyles(m3eTheme.textTheme), + ); + }); + + test('ThemeData.textTheme overrides do not apply to emphasized styles', () { + // ThemeData merges a custom text theme field by field, so overriding a + // baseline style does not change its emphasized variant. + final customTextTheme = ThemeData( + textTheme: const TextTheme(bodyMedium: TextStyle(fontFamily: 'Custom')), + ); + expect(customTextTheme.textTheme.bodyMedium!.fontFamily, 'Custom'); + expect(customTextTheme.textTheme.bodyMediumEmphasized!.fontFamily, 'Roboto'); + + // ThemeData.fontFamily is applied to every style, including emphasized styles. + final customFontFamily = ThemeData(fontFamily: 'Custom'); + for (final style in [ + ..._baselineStyles(customFontFamily.textTheme), + ..._emphasizedStyles(customFontFamily.textTheme), + ]) { + expect(style!.fontFamily, 'Custom'); + } + }); }); } From 13409891b8c5470c34b04ad81b02c6d7c5b56107 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:34:29 -0700 Subject: [PATCH 07/11] Document limitation about custom text themes and add test --- packages/material_ui/lib/src/theme_data.dart | 9 ++++++ .../material_ui/test/typography_test.dart | 28 ++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/material_ui/lib/src/theme_data.dart b/packages/material_ui/lib/src/theme_data.dart index a92c925751b5..273cca3152cf 100644 --- a/packages/material_ui/lib/src/theme_data.dart +++ b/packages/material_ui/lib/src/theme_data.dart @@ -1389,6 +1389,15 @@ class ThemeData with Diagnosticable { final TextTheme primaryTextTheme; /// Text with a color that contrasts with the card and canvas colors. + /// + /// When a [TextTheme] is passed to the [ThemeData] constructor, it is merged + /// field by field with the default text theme from [typography]. Overriding a + /// baseline style (such as [TextTheme.bodyMedium]) does not change its + /// emphasized variant (such as [TextTheme.bodyMediumEmphasized]), which keeps + /// the default typeface. To use a custom font for every style, including the + /// emphasized styles, use the `fontFamily` and `fontFamilyFallback` + /// constructor parameters, or provide the emphasized styles in the custom + /// [TextTheme]. final TextTheme textTheme; /// The color and geometry [TextTheme] values used to configure [textTheme]. diff --git a/packages/material_ui/test/typography_test.dart b/packages/material_ui/test/typography_test.dart index b4b0df2c6cc4..a5a63dd8d7e5 100644 --- a/packages/material_ui/test/typography_test.dart +++ b/packages/material_ui/test/typography_test.dart @@ -804,14 +804,28 @@ void main() { ); expect(customTextTheme.textTheme.bodyMedium!.fontFamily, 'Custom'); expect(customTextTheme.textTheme.bodyMediumEmphasized!.fontFamily, 'Roboto'); + }); - // ThemeData.fontFamily is applied to every style, including emphasized styles. - final customFontFamily = ThemeData(fontFamily: 'Custom'); - for (final style in [ - ..._baselineStyles(customFontFamily.textTheme), - ..._emphasizedStyles(customFontFamily.textTheme), - ]) { - expect(style!.fontFamily, 'Custom'); + test('ThemeData fontFamily and fontFamilyFallback apply to emphasized styles', () { + const fontFamilyFallback = ['CustomFallback1', 'CustomFallback2']; + for (final TargetPlatform platform in TargetPlatform.values) { + final theme = ThemeData( + platform: platform, + fontFamily: 'Custom', + fontFamilyFallback: fontFamilyFallback, + ); + final List baseline = _baselineStyles(theme.textTheme); + final List emphasized = _emphasizedStyles(theme.textTheme); + for (var i = 0; i < baseline.length; i++) { + expect(baseline[i]!.fontFamily, 'Custom', reason: '$platform'); + expect(baseline[i]!.fontFamilyFallback, fontFamilyFallback, reason: '$platform'); + expect(emphasized[i]!.fontFamily, baseline[i]!.fontFamily, reason: '$platform'); + expect( + emphasized[i]!.fontFamilyFallback, + baseline[i]!.fontFamilyFallback, + reason: '$platform', + ); + } } }); }); From 272378b2f236098d7d29f72dc0c890b9d1d951f0 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:17:10 -0700 Subject: [PATCH 08/11] Add missing documentation --- packages/material_ui/lib/src/typography.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/material_ui/lib/src/typography.dart b/packages/material_ui/lib/src/typography.dart index 6356e0d1bb28..9cefd7b195e7 100644 --- a/packages/material_ui/lib/src/typography.dart +++ b/packages/material_ui/lib/src/typography.dart @@ -71,6 +71,10 @@ enum ScriptCategory { /// font](https://developer.apple.com/design/human-interface-guidelines/typography/) /// fonts as used by Apple on iOS. /// +/// [Typography.material2025] instead uses typeface text themes, such as +/// [mountainView] and [cupertino], which provide the same fonts without any +/// color. Their colors are derived from a [ColorScheme]. +/// /// Four sets of geometry themes are provided: 2014, 2018, 2021, and 2025: /// /// * The 2014 themes correspond to the original version of the Material Design From caf013b30c23beac9d01c565e1019131643e354a Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:17:18 -0700 Subject: [PATCH 09/11] Add example --- packages/material_ui/example/lib/main.dart | 7 + .../example/lib/typography/typography.0.dart | 152 ++++++++++++++++++ .../test/typography/typography.0_test.dart | 40 +++++ 3 files changed, 199 insertions(+) create mode 100644 packages/material_ui/example/lib/typography/typography.0.dart create mode 100644 packages/material_ui/example/test/typography/typography.0_test.dart diff --git a/packages/material_ui/example/lib/main.dart b/packages/material_ui/example/lib/main.dart index f7c9517f6870..7197cf261018 100644 --- a/packages/material_ui/example/lib/main.dart +++ b/packages/material_ui/example/lib/main.dart @@ -29,6 +29,7 @@ import 'progress_indicator/circular_progress_indicator.0.dart' as circular_progress_indicator_0; import 'dropdown_menu/dropdown_menu.1.dart' as dropdown_menu_1; import 'material_shapes/material_shapes.0.dart' as material_shapes_0; +import 'typography/typography.0.dart' as typography_0; import 'navigation_bar/navigation_bar.1.dart' as navigation_bar_1; import 'navigation_rail/navigation_rail.0.dart' as navigation_rail_0; import 'navigation_drawer/navigation_drawer.0.dart' as navigation_drawer_0; @@ -245,6 +246,12 @@ class ExampleApp extends StatelessWidget { title: 'Tooltip', builder: (BuildContext context) => const tooltip_0.TooltipExampleApp(), ), + _Example( + filepath: 'typography/typography.0.dart', + title: 'typography', + builder: (BuildContext context) => + const typography_0.TypographyExampleApp(), + ), ]; @override diff --git a/packages/material_ui/example/lib/typography/typography.0.dart b/packages/material_ui/example/lib/typography/typography.0.dart new file mode 100644 index 000000000000..87ea388f3473 --- /dev/null +++ b/packages/material_ui/example/lib/typography/typography.0.dart @@ -0,0 +1,152 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// #region body +import 'package:material_ui/material_ui.dart'; + +/// Flutter code sample for [Typography.material2025]. + +void main() { + runApp(const TypographyExampleApp()); +} + +class TypographyExampleApp extends StatelessWidget { + const TypographyExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + // The default typography provides an emphasized variant of each text + // style, such as TextTheme.titleMediumEmphasized. + return MaterialApp(theme: ThemeData(), home: const TypographyExample()); + } +} + +enum TypeStyleVariant { baseline, emphasized } + +class TypographyExample extends StatefulWidget { + const TypographyExample({super.key}); + + @override + State createState() => _TypographyExampleState(); +} + +class _TypographyExampleState extends State { + TypeStyleVariant _variant = TypeStyleVariant.baseline; + + @override + Widget build(BuildContext context) { + final TextTheme textTheme = TextTheme.of(context); + final ColorScheme colorScheme = ColorScheme.of(context); + final emphasized = _variant == TypeStyleVariant.emphasized; + + final styles = <(String, TextStyle?, TextStyle?)>[ + ( + 'Display Large', + textTheme.displayLarge, + textTheme.displayLargeEmphasized, + ), + ( + 'Display Medium', + textTheme.displayMedium, + textTheme.displayMediumEmphasized, + ), + ( + 'Display Small', + textTheme.displaySmall, + textTheme.displaySmallEmphasized, + ), + ( + 'Headline Large', + textTheme.headlineLarge, + textTheme.headlineLargeEmphasized, + ), + ( + 'Headline Medium', + textTheme.headlineMedium, + textTheme.headlineMediumEmphasized, + ), + ( + 'Headline Small', + textTheme.headlineSmall, + textTheme.headlineSmallEmphasized, + ), + ('Title Large', textTheme.titleLarge, textTheme.titleLargeEmphasized), + ('Title Medium', textTheme.titleMedium, textTheme.titleMediumEmphasized), + ('Title Small', textTheme.titleSmall, textTheme.titleSmallEmphasized), + ('Label Large', textTheme.labelLarge, textTheme.labelLargeEmphasized), + ('Label Medium', textTheme.labelMedium, textTheme.labelMediumEmphasized), + ('Label Small', textTheme.labelSmall, textTheme.labelSmallEmphasized), + ('Body Large', textTheme.bodyLarge, textTheme.bodyLargeEmphasized), + ('Body Medium', textTheme.bodyMedium, textTheme.bodyMediumEmphasized), + ('Body Small', textTheme.bodySmall, textTheme.bodySmallEmphasized), + ]; + + return Scaffold( + appBar: AppBar(title: const Text('Typography')), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: SegmentedButton( + segments: const >[ + ButtonSegment( + value: TypeStyleVariant.baseline, + label: Text('Baseline'), + ), + ButtonSegment( + value: TypeStyleVariant.emphasized, + label: Text('Emphasized'), + ), + ], + selected: {_variant}, + onSelectionChanged: (Set selection) { + setState(() { + _variant = selection.single; + }); + }, + ), + ), + Expanded( + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 16), + children: [ + for (final ( + String name, + TextStyle? baseline, + TextStyle? emphasizedStyle, + ) + in styles) + Card.outlined( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8, + children: [ + Text( + emphasized + ? 'md.sys.typescale.emphasized' + : 'md.sys.typescale', + style: textTheme.labelSmall?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + Text( + name, + style: emphasized ? emphasizedStyle : baseline, + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +// #endregion body diff --git a/packages/material_ui/example/test/typography/typography.0_test.dart b/packages/material_ui/example/test/typography/typography.0_test.dart new file mode 100644 index 000000000000..686712ac6367 --- /dev/null +++ b/packages/material_ui/example/test/typography/typography.0_test.dart @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; +import 'package:material_ui_examples/typography/typography.0.dart' as example; + +void main() { + testWidgets('Toggles between baseline and emphasized text styles', ( + WidgetTester tester, + ) async { + await tester.pumpWidget(const example.TypographyExampleApp()); + + final TextTheme textTheme = TextTheme.of( + tester.element(find.byType(example.TypographyExample)), + ); + TextStyle? styleOf(String text) => + tester.widget(find.text(text)).style; + + expect(find.text('md.sys.typescale'), findsWidgets); + expect(find.text('md.sys.typescale.emphasized'), findsNothing); + expect(styleOf('Display Large'), textTheme.displayLarge); + + await tester.tap(find.text('Emphasized')); + await tester.pumpAndSettle(); + + expect(find.text('md.sys.typescale'), findsNothing); + expect(find.text('md.sys.typescale.emphasized'), findsWidgets); + expect(styleOf('Display Large'), textTheme.displayLargeEmphasized); + + await tester.scrollUntilVisible(find.text('Title Medium'), 200); + expect(styleOf('Title Medium'), textTheme.titleMediumEmphasized); + + await tester.tap(find.text('Baseline')); + await tester.pumpAndSettle(); + + expect(styleOf('Title Medium'), textTheme.titleMedium); + }); +} From bc08045723a1f8db23ee34b6a90f0be512fc4bf3 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:22:38 -0700 Subject: [PATCH 10/11] Capitalize Typography --- packages/material_ui/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material_ui/example/lib/main.dart b/packages/material_ui/example/lib/main.dart index 7197cf261018..75d8a670960d 100644 --- a/packages/material_ui/example/lib/main.dart +++ b/packages/material_ui/example/lib/main.dart @@ -248,7 +248,7 @@ class ExampleApp extends StatelessWidget { ), _Example( filepath: 'typography/typography.0.dart', - title: 'typography', + title: 'Typography', builder: (BuildContext context) => const typography_0.TypographyExampleApp(), ), From f6f8402d2b2edc933d4e773c3d8d3eabfb181e74 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 29 Oct 2026 09:20:14 -0700 Subject: [PATCH 11/11] Remove extra period --- packages/material_ui/lib/src/text_theme.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material_ui/lib/src/text_theme.dart b/packages/material_ui/lib/src/text_theme.dart index c8482f22c9c6..7ee7f015d6d6 100644 --- a/packages/material_ui/lib/src/text_theme.dart +++ b/packages/material_ui/lib/src/text_theme.dart @@ -32,7 +32,7 @@ import 'theme.dart'; /// ![](https://lh3.googleusercontent.com/Yvngs5mQSjXa_9T4X3JDucO62c5hdZHPDa7qeRH6DsJQvGr_q7EBrTkhkPiQd9OeR1v_Uk38Cjd9nUpP3nevDyHpKWuXSfQ1Gq78bOnBN7sr=s0) /// /// The Material Design typography scheme was significantly changed in the -/// Material 3 (2021) version of the specification. +/// Material 3 (2021) version of the specification /// ([https://m3.material.io/styles/typography/tokens](https://m3.material.io/styles/typography/tokens)). /// /// The current Material 3 Expressive (2025) version of the specification further