When is the full textTheme available? #212
-
Hello, I am trying to alter the global ListTile titleTextStyle and subtitleListStyle but I noticed that not all of the bodyLarge and bodyMedium TextStyle properties are available immediately. I am forced to manually include the default TextStyle properties in the .copyWith along with the ones I want to add. Is there a better way to implement this?
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Thanks for your question. This is a good one, and it is a fairly common one for Flutter generally too, as it applies to it as well. It is actually surprisingly complicated how and when the default text geometry is applied to all the TLDR and short answerAs for using FlexColorScheme and when the full This means after the The long story and why it is this wayThe font sizes in the The default text style sizes are applied by the static as shown here: static ThemeData of(BuildContext context) {
final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme;
return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
} The /// A characterization of the of a [TextTheme]'s glyphs that is used to define
/// its localized [TextStyle] geometry for [ThemeData.textTheme].
///
/// The script category defines the overall geometry of a [TextTheme] for
/// the [Typography.geometryThemeFor] method in terms of the
/// three language categories defined in <https://material.io/go/design-typography>.
///
/// Generally speaking, font sizes for [ScriptCategory.tall] and
/// [ScriptCategory.dense] scripts - for text styles that are smaller than the
/// title style - are one unit larger than they are for
/// [ScriptCategory.englishLike] scripts.
enum ScriptCategory {
/// The languages of Western, Central, and Eastern Europe and much of
/// Africa are typically written in the Latin alphabet. Vietnamese is a
/// notable exception in that, while it uses a localized form of the Latin
/// writing system, its accented glyphs can be much taller than those
/// found in Western European languages. The Greek and Cyrillic writing
/// systems are very similar to Latin.
englishLike,
/// Language scripts that require extra line height to accommodate larger
/// glyphs, including Chinese, Japanese, and Korean.
dense,
/// Language scripts that require extra line height to accommodate
/// larger glyphs, including South and Southeast Asian and
/// Middle-Eastern languages, like Arabic, Hindi, Telugu, Thai, and
/// Vietnamese.
tall,
} Which one gets used depends on the user's locale. These are defined for each locale in the From the Finally, the last line's /// Returns a new theme built by merging the text geometry provided by the
/// [localTextGeometry] theme with the [baseTheme].
///
/// For those text styles in the [baseTheme] whose [TextStyle.inherit] is set
/// to true, the returned theme's text styles inherit the geometric properties
/// of [localTextGeometry]. The resulting text styles' [TextStyle.inherit] is
/// set to those provided by [localTextGeometry].
static ThemeData localize(ThemeData baseTheme, TextTheme localTextGeometry) {
// WARNING: this method memoizes the result in a cache based on the
// previously seen baseTheme and localTextGeometry. Memoization is safe
// because all inputs and outputs of this function are deeply immutable, and
// the computations are referentially transparent. It only short-circuits
// the computation if the new inputs are identical() to the previous ones.
// It does not use the == operator, which performs a costly deep comparison.
//
// When changing this method, make sure the memoization logic is correct.
// Remember:
//
// There are only two hard things in Computer Science: cache invalidation
// and naming things. -- Phil Karlton
return _localizedThemeDataCache.putIfAbsent(
_IdentityThemeDataCacheKey(baseTheme, localTextGeometry),
() {
return baseTheme.copyWith(
primaryTextTheme: localTextGeometry.merge(baseTheme.primaryTextTheme),
textTheme: localTextGeometry.merge(baseTheme.textTheme),
);
},
);
} Basically merging in the locale based text style geometries for the In the return ScaffoldMessenger(
key: widget.scaffoldMessengerKey,
child: DefaultSelectionStyle(
selectionColor: effectiveSelectionColor,
cursorColor: effectiveCursorColor,
child: AnimatedTheme(
data: theme,
duration: widget.themeAnimationDuration,
curve: widget.themeAnimationCurve,
child: widget.builder != null
? Builder(
builder: (BuildContext context) {
// Why are we surrounding a builder with a builder?
//
// The widget.builder may contain code that invokes
// Theme.of(), which should return the theme we selected
// above in AnimatedTheme. However, if we invoke
// widget.builder() directly as the child of AnimatedTheme
// then there is no Context separating them, and the
// widget.builder() will not find the theme. Therefore, we
// surround widget.builder with yet another builder so that
// a context separates them and Theme.of() correctly
// resolves to the theme we passed to AnimatedTheme.
return widget.builder!(context, child);
},
)
: child ?? const SizedBox.shrink(),
),
),
);
} Yes, I agree, the process used to apply the default text style geometry to all Options?If you want to use sizes from the Optionally, if you do know you will only use e.g. a locale where the
The way I usually do custom sizes for component themes is via custom Custom TextTheme and TextStylesIf you do provide a custom Likewise, if you define custom Using a full custom I am referring to this at the end of the answer: final TextStyle openSansRegular =
GoogleFonts.openSans(fontWeight: FontWeight.w400);
final TextStyle openSansMedium =
GoogleFonts.openSans(fontWeight: FontWeight.w500);
final TextStyle openSansBold =
GoogleFonts.openSans(fontWeight: FontWeight.w700);
final TextTheme customOpenSansTextTheme = TextTheme(
displayLarge:
openSansBold.copyWith(fontSize: 50), // Custom bold and size (Regular is default)
displayMedium: openSansRegular, // Regular is default
displaySmall: openSansRegular, // Regular is default
headlineLarge: openSansRegular, // Regular is default
headlineMedium: openSansRegular, // Regular is default
headlineSmall: openSansRegular, // Regular is default
titleLarge: openSansBold, // Custom bold (Regular is default)
titleMedium: openSansBold, // Custom bold (Medium is default)
titleSmall: openSansBold, // Custom bold (Medium is default)
bodyLarge: openSansRegular, // Regular is default
bodyMedium: openSansRegular, // Regular is default
bodySmall: openSansRegular, // Regular is default
labelLarge: openSansBold, // Custom bold (Medium is default)
labelMedium: openSansBold, // Custom bold (Medium is default)
labelSmall: openSansMedium, // Medium is default
); Instead, of just defining the three text styles with regular, medium and bold, you could define one for every If you leave the color out of the text styles, the
With the above discussed setup, you can use each style with its size in any component theme If you want a custom You can find an example of that in the Theming workshop from the Flutter'N Friends conference in Stockholm. Repo is here https://github.com/rydmike/theming_workshop/tree/31-done and here is a theme extension with custom
Hope this helps! 💙 Yes, TextTheme is a bit complex in Flutter 😄 |
Beta Was this translation helpful? Give feedback.
-
Hi @rydmike! First of all, wow! Thank you very much for the detailed answer. Your knowledge depth is very impressive. We were manually applying We were able to resolve our primary issue (of not having the desired text color for the final colorSchemeLight = ColorScheme(
brightness: Brightness.light,
primary: CustomColorsScheme.light.primary!,
onPrimary: CustomColorsScheme.light.onPrimary!,
secondary: CustomColorsScheme.light.secondary!,
onSecondary: CustomColorsScheme.light.onSecondary!,
secondaryContainer: CustomColorsScheme.light.secondaryContainer!,
onSecondaryContainer: CustomColorsScheme.light.onSecondaryContainer,
error: CustomColorsScheme.light.error!,
onError: CustomColorsScheme.light.onErrorContainerHigh!,
background: CustomColorsScheme.light.background!,
onBackground: CustomColorsScheme.light.onBackground!,
surface: CustomColorsScheme.light.surface!,
onSurface: CustomColorsScheme.light.onSurface!,
surfaceVariant: CustomColorsScheme.light.surfaceVariant!,
onSurfaceVariant: CustomColorsScheme.light.onSurfaceVariant!,
outline: CustomColorsScheme.light.outline,
outlineVariant: CustomColorsScheme.light.outlineVariant,
inverseSurface: CustomColorsScheme.light.inverseSurface!,
inversePrimary: CustomColorsScheme.light.inversePrimary!,
surfaceTint: Colors.transparent,
);
final flexThemeLight = FlexThemeData.light(
colorScheme: colorSchemeLight,
useMaterial3: true,
useMaterial3ErrorColors: true,
appBarBackground: CustomColorsScheme.light.surface2,
extensions: const <ThemeExtension<dynamic>>[CustomColorsScheme.light],
); Then overriding any properties defaults we wanted to alter using Again, thank you very much for the insight into TextTheme and TextStyle and the options provided. I will definitely be checking out the Theming workshop. |
Beta Was this translation helpful? Give feedback.
Hi @jgarciamccausland,
Thanks for your question. This is a good one, and it is a fairly common one for Flutter generally too, as it applies to it as well.
It is actually surprisingly complicated how and when the default text geometry is applied to all the
TextStyle
s intextTheme
andprimaryTextTheme
inThemeData.of(context)
, but never to an object created by anyThemeData
factory.TLDR and short answer
As for using FlexColorScheme and when the full
textTheme
is available, the answer is simple.It is the same as for any Flutter app using
MaterialApp
.This means after the
MaterialApp
widget has been created and localization applied. The text themes in FlexColorScheme's createdThemeData
wor…